From: Chris Mason Subject: slab testing module Patch-mainline: probably never --- drivers/char/Kconfig | 5 + drivers/char/Makefile | 1 drivers/char/crasher.c | 228 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 234 insertions(+) --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -1129,5 +1129,10 @@ config RAMOOPS This enables panic and oops messages to be logged to a circular buffer in RAM where it can be read back at some later point. +config CRASHER + tristate "Crasher Module" + help + Slab cache memory tester. Only use this as a module + endmenu --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -108,6 +108,7 @@ obj-$(CONFIG_IPMI_HANDLER) += ipmi/ obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o obj-$(CONFIG_TCG_TPM) += tpm/ +obj-$(CONFIG_CRASHER) += crasher.o obj-$(CONFIG_PS3_FLASH) += ps3flash.o obj-$(CONFIG_RAMOOPS) += ramoops.o --- /dev/null +++ b/drivers/char/crasher.c @@ -0,0 +1,228 @@ +/* + * crasher.c, it breaks things + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int module_exiting; +static struct completion startup = COMPLETION_INITIALIZER(startup); +static unsigned long rand_seed = 152L; +static unsigned long seed = 152L; +static int threads = 1; +static int call_panic, call_bug, call_warn; +static int trap_null, call_null, jump_null; +static long trap_read, trap_write, call_bad, jump_bad; + +module_param(seed, ulong, 0); +module_param(call_panic, bool, 0); +module_param(call_bug, bool, 0); +module_param(call_warn, bool, 0); +module_param(trap_null, bool, 0); +module_param(trap_read, long, 0); +module_param(trap_write, long, 0); +module_param(call_null, bool, 0); +module_param(call_bad, long, 0); +module_param(jump_null, bool, 0); +module_param(jump_bad, long, 0); +module_param(threads, int, 0); +MODULE_PARM_DESC(seed, "random seed for memory tests"); +MODULE_PARM_DESC(call_panic, "test option. call panic() and render the system unusable."); +MODULE_PARM_DESC(call_bug, "test option. call BUG() and render the system unusable."); +MODULE_PARM_DESC(call_warn, "test option. call WARN() and leave the system usable."); +MODULE_PARM_DESC(trap_null, "test option. dereference a NULL pointer to simulate a crash and render the system unusable."); +MODULE_PARM_DESC(trap_read, "test option. read from an invalid address to simulate a crash and render the system unusable."); +MODULE_PARM_DESC(trap_write, "test option. write to an invalid address to simulate a crash and render the system unusable."); +MODULE_PARM_DESC(call_null, "test option. call a NULL pointer to simulate a crash and render the system unusable."); +MODULE_PARM_DESC(call_bad, "test option. call an invalid address to simulate a crash and render the system unusable."); +MODULE_PARM_DESC(jump_null, "test option. jump to a NULL pointer to simulate a crash and render the system unusable."); +MODULE_PARM_DESC(jump_bad, "test option. jump to an invalid address to simulate a crash and render the system unusable."); +MODULE_PARM_DESC(threads, "number of threads to run"); +MODULE_LICENSE("GPL"); + +#define NUM_ALLOC 24 +#define NUM_SIZES 8 +static int sizes[] = { 32, 64, 128, 192, 256, 1024, 2048, 4096 }; + +struct mem_buf { + char *buf; + int size; +}; + +static unsigned long crasher_random(void) +{ + rand_seed = rand_seed*69069L+1; + return rand_seed^jiffies; +} + +void crasher_srandom(unsigned long entropy) +{ + rand_seed ^= entropy; + crasher_random(); +} + +static char *mem_alloc(int size) { + char *p = kmalloc(size, GFP_KERNEL); + int i; + if (!p) + return p; + for (i = 0 ; i < size; i++) + p[i] = (i % 119) + 8; + return p; +} + +static void mem_check(char *p, int size) { + int i; + if (!p) + return; + for (i = 0 ; i < size; i++) { + if (p[i] != ((i % 119) + 8)) { + printk(KERN_CRIT "verify error at %lX offset %d " + " wanted %d found %d size %d\n", + (unsigned long)(p + i), i, (i % 119) + 8, + p[i], size); + } + } + // try and trigger slab poisoning for people using this buffer + // wrong + memset(p, 0, size); +} + +static void mem_verify(void) { + struct mem_buf bufs[NUM_ALLOC]; + struct mem_buf *b; + int index; + int size; + unsigned long sleep; + memset(bufs, 0, sizeof(struct mem_buf) * NUM_ALLOC); + while(!module_exiting) { + index = crasher_random() % NUM_ALLOC; + b = bufs + index; + if (b->size) { + mem_check(b->buf, b->size); + kfree(b->buf); + b->buf = NULL; + b->size = 0; + } else { + size = crasher_random() % NUM_SIZES; + size = sizes[size]; + b->buf = mem_alloc(size); + b->size = size; + } + sleep = crasher_random() % (HZ / 10); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(sleep); + set_current_state(TASK_RUNNING); + } + for (index = 0 ; index < NUM_ALLOC ; index++) { + b = bufs + index; + if (b->size) { + mem_check(b->buf, b->size); + kfree(b->buf); + } + } +} + +static int crasher_thread(void *unused) +{ + daemonize("crasher"); + complete(&startup); + mem_verify(); + complete(&startup); + return 0; +} + +static int __init crasher_init(void) +{ + int i; + init_completion(&startup); + crasher_srandom(seed); + + if (call_panic) { + panic("test panic from crasher module. Good Luck.\n"); + return -EFAULT; + } + if (call_bug) { + printk("triggering BUG\n"); + BUG_ON(1); + return -EFAULT; + } + if (WARN(call_warn, "triggering WARN\n")) + return -EFAULT; + + if (trap_null) { + volatile char *p = NULL; + printk("dereferencing NULL pointer.\n"); + p[0] = '\n'; + return -EFAULT; + } + if (trap_read) { + const volatile char *p = (char *)trap_read; + printk("reading from invalid(?) address %p.\n", p); + return p[0] ? -EFAULT : -EACCES; + } + if (trap_write) { + volatile char *p = (char *)trap_write; + printk("writing to invalid(?) address %p.\n", p); + p[0] = ' '; + return -EFAULT; + } + + if (call_null) { + void(*f)(void) = NULL; + printk("calling NULL pointer.\n"); + f(); + return -EFAULT; + } + if (call_bad) { + void(*f)(void) = (void(*)(void))call_bad; + printk("calling invalid(?) address %p.\n", f); + f(); + return -EFAULT; + } + + /* These two depend on the compiler doing tail call optimization. */ + if (jump_null) { + int(*f)(void) = NULL; + printk("jumping to NULL.\n"); + return f(); + } + if (jump_bad) { + int(*f)(void) = (int(*)(void))jump_bad; + printk("jumping to invalid(?) address %p.\n", f); + return f(); + } + + printk("crasher module (%d threads). Testing sizes: ", threads); + for (i = 0 ; i < NUM_SIZES ; i++) + printk("%d ", sizes[i]); + printk("\n"); + + for (i = 0 ; i < threads ; i++) + kernel_thread(crasher_thread, crasher_thread, + CLONE_FS | CLONE_FILES); + for (i = 0 ; i < threads ; i++) + wait_for_completion(&startup); + return 0; +} + +static void __exit crasher_exit(void) +{ + int i; + module_exiting = 1; + for (i = 0 ; i < threads ; i++) + wait_for_completion(&startup); + printk("all crasher threads done\n"); + return; +} + +module_init(crasher_init); +module_exit(crasher_exit);