lkdtm: use struct arrays instead of enums
This removes the use of enums in favor of much more readable and compact structure arrays. This requires changing all the enum passing to pointers instead, but the results are much cleaner. Signed-off-by: Kees Cook <keescook@chromium.org>
This commit is contained in:
parent
f2c6edc1ed
commit
c479e3fd88
|
@ -53,8 +53,16 @@
|
||||||
|
|
||||||
#define DEFAULT_COUNT 10
|
#define DEFAULT_COUNT 10
|
||||||
|
|
||||||
static int lkdtm_parse_commandline(void);
|
|
||||||
static void lkdtm_handler(void);
|
static void lkdtm_handler(void);
|
||||||
|
static int lkdtm_debugfs_open(struct inode *inode, struct file *file);
|
||||||
|
static ssize_t lkdtm_debugfs_read(struct file *f, char __user *user_buf,
|
||||||
|
size_t count, loff_t *off);
|
||||||
|
static ssize_t direct_entry(struct file *f, const char __user *user_buf,
|
||||||
|
size_t count, loff_t *off);
|
||||||
|
static ssize_t lkdtm_debugfs_entry(struct file *f,
|
||||||
|
const char __user *user_buf,
|
||||||
|
size_t count, loff_t *off);
|
||||||
|
|
||||||
|
|
||||||
/* jprobe entry point handlers. */
|
/* jprobe entry point handlers. */
|
||||||
static unsigned int jp_do_irq(unsigned int irq)
|
static unsigned int jp_do_irq(unsigned int irq)
|
||||||
|
@ -121,118 +129,114 @@ static int jp_generic_ide_ioctl(ide_drive_t *drive, struct file *file,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
enum cname {
|
|
||||||
CN_INVALID,
|
/* Crash points */
|
||||||
CN_INT_HARDWARE_ENTRY,
|
struct crashpoint {
|
||||||
CN_INT_HW_IRQ_EN,
|
const char *name;
|
||||||
CN_INT_TASKLET_ENTRY,
|
const struct file_operations fops;
|
||||||
CN_FS_DEVRW,
|
struct jprobe jprobe;
|
||||||
CN_MEM_SWAPOUT,
|
|
||||||
CN_TIMERADD,
|
|
||||||
CN_SCSI_DISPATCH_CMD,
|
|
||||||
CN_IDE_CORE_CP,
|
|
||||||
CN_DIRECT,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ctype {
|
#define CRASHPOINT(_name, _write, _symbol, _entry) \
|
||||||
CT_NONE,
|
{ \
|
||||||
CT_PANIC,
|
.name = _name, \
|
||||||
CT_BUG,
|
.fops = { \
|
||||||
CT_WARNING,
|
.read = lkdtm_debugfs_read, \
|
||||||
CT_EXCEPTION,
|
.llseek = generic_file_llseek, \
|
||||||
CT_LOOP,
|
.open = lkdtm_debugfs_open, \
|
||||||
CT_OVERFLOW,
|
.write = _write, \
|
||||||
CT_CORRUPT_STACK,
|
}, \
|
||||||
CT_UNALIGNED_LOAD_STORE_WRITE,
|
.jprobe = { \
|
||||||
CT_OVERWRITE_ALLOCATION,
|
.kp.symbol_name = _symbol, \
|
||||||
CT_WRITE_AFTER_FREE,
|
.entry = (kprobe_opcode_t *)_entry, \
|
||||||
CT_READ_AFTER_FREE,
|
}, \
|
||||||
CT_WRITE_BUDDY_AFTER_FREE,
|
}
|
||||||
CT_READ_BUDDY_AFTER_FREE,
|
|
||||||
CT_SOFTLOCKUP,
|
/* Define the possible places where we can trigger a crash point. */
|
||||||
CT_HARDLOCKUP,
|
struct crashpoint crashpoints[] = {
|
||||||
CT_SPINLOCKUP,
|
CRASHPOINT("DIRECT", direct_entry,
|
||||||
CT_HUNG_TASK,
|
NULL, NULL),
|
||||||
CT_EXEC_DATA,
|
#ifdef CONFIG_KPROBES
|
||||||
CT_EXEC_STACK,
|
CRASHPOINT("INT_HARDWARE_ENTRY", lkdtm_debugfs_entry,
|
||||||
CT_EXEC_KMALLOC,
|
"do_IRQ", jp_do_irq),
|
||||||
CT_EXEC_VMALLOC,
|
CRASHPOINT("INT_HW_IRQ_EN", lkdtm_debugfs_entry,
|
||||||
CT_EXEC_RODATA,
|
"handle_IRQ_event", jp_handle_irq_event),
|
||||||
CT_EXEC_USERSPACE,
|
CRASHPOINT("INT_TASKLET_ENTRY", lkdtm_debugfs_entry,
|
||||||
CT_ACCESS_USERSPACE,
|
"tasklet_action", jp_tasklet_action),
|
||||||
CT_WRITE_RO,
|
CRASHPOINT("FS_DEVRW", lkdtm_debugfs_entry,
|
||||||
CT_WRITE_RO_AFTER_INIT,
|
"ll_rw_block", jp_ll_rw_block),
|
||||||
CT_WRITE_KERN,
|
CRASHPOINT("MEM_SWAPOUT", lkdtm_debugfs_entry,
|
||||||
CT_ATOMIC_UNDERFLOW,
|
"shrink_inactive_list", jp_shrink_inactive_list),
|
||||||
CT_ATOMIC_OVERFLOW,
|
CRASHPOINT("TIMERADD", lkdtm_debugfs_entry,
|
||||||
CT_USERCOPY_HEAP_SIZE_TO,
|
"hrtimer_start", jp_hrtimer_start),
|
||||||
CT_USERCOPY_HEAP_SIZE_FROM,
|
CRASHPOINT("SCSI_DISPATCH_CMD", lkdtm_debugfs_entry,
|
||||||
CT_USERCOPY_HEAP_FLAG_TO,
|
"scsi_dispatch_cmd", jp_scsi_dispatch_cmd),
|
||||||
CT_USERCOPY_HEAP_FLAG_FROM,
|
# ifdef CONFIG_IDE
|
||||||
CT_USERCOPY_STACK_FRAME_TO,
|
CRASHPOINT("IDE_CORE_CP", lkdtm_debugfs_entry,
|
||||||
CT_USERCOPY_STACK_FRAME_FROM,
|
"generic_ide_ioctl", jp_generic_ide_ioctl),
|
||||||
CT_USERCOPY_STACK_BEYOND,
|
# endif
|
||||||
CT_USERCOPY_KERNEL,
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static char* cp_name[] = {
|
|
||||||
"INVALID",
|
/* Crash types. */
|
||||||
"INT_HARDWARE_ENTRY",
|
struct crashtype {
|
||||||
"INT_HW_IRQ_EN",
|
const char *name;
|
||||||
"INT_TASKLET_ENTRY",
|
void (*func)(void);
|
||||||
"FS_DEVRW",
|
|
||||||
"MEM_SWAPOUT",
|
|
||||||
"TIMERADD",
|
|
||||||
"SCSI_DISPATCH_CMD",
|
|
||||||
"IDE_CORE_CP",
|
|
||||||
"DIRECT",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static char* cp_type[] = {
|
#define CRASHTYPE(_name) \
|
||||||
"NONE",
|
{ \
|
||||||
"PANIC",
|
.name = __stringify(_name), \
|
||||||
"BUG",
|
.func = lkdtm_ ## _name, \
|
||||||
"WARNING",
|
}
|
||||||
"EXCEPTION",
|
|
||||||
"LOOP",
|
/* Define the possible types of crashes that can be triggered. */
|
||||||
"OVERFLOW",
|
struct crashtype crashtypes[] = {
|
||||||
"CORRUPT_STACK",
|
CRASHTYPE(PANIC),
|
||||||
"UNALIGNED_LOAD_STORE_WRITE",
|
CRASHTYPE(BUG),
|
||||||
"OVERWRITE_ALLOCATION",
|
CRASHTYPE(WARNING),
|
||||||
"WRITE_AFTER_FREE",
|
CRASHTYPE(EXCEPTION),
|
||||||
"READ_AFTER_FREE",
|
CRASHTYPE(LOOP),
|
||||||
"WRITE_BUDDY_AFTER_FREE",
|
CRASHTYPE(OVERFLOW),
|
||||||
"READ_BUDDY_AFTER_FREE",
|
CRASHTYPE(CORRUPT_STACK),
|
||||||
"SOFTLOCKUP",
|
CRASHTYPE(UNALIGNED_LOAD_STORE_WRITE),
|
||||||
"HARDLOCKUP",
|
CRASHTYPE(OVERWRITE_ALLOCATION),
|
||||||
"SPINLOCKUP",
|
CRASHTYPE(WRITE_AFTER_FREE),
|
||||||
"HUNG_TASK",
|
CRASHTYPE(READ_AFTER_FREE),
|
||||||
"EXEC_DATA",
|
CRASHTYPE(WRITE_BUDDY_AFTER_FREE),
|
||||||
"EXEC_STACK",
|
CRASHTYPE(READ_BUDDY_AFTER_FREE),
|
||||||
"EXEC_KMALLOC",
|
CRASHTYPE(SOFTLOCKUP),
|
||||||
"EXEC_VMALLOC",
|
CRASHTYPE(HARDLOCKUP),
|
||||||
"EXEC_RODATA",
|
CRASHTYPE(SPINLOCKUP),
|
||||||
"EXEC_USERSPACE",
|
CRASHTYPE(HUNG_TASK),
|
||||||
"ACCESS_USERSPACE",
|
CRASHTYPE(EXEC_DATA),
|
||||||
"WRITE_RO",
|
CRASHTYPE(EXEC_STACK),
|
||||||
"WRITE_RO_AFTER_INIT",
|
CRASHTYPE(EXEC_KMALLOC),
|
||||||
"WRITE_KERN",
|
CRASHTYPE(EXEC_VMALLOC),
|
||||||
"ATOMIC_UNDERFLOW",
|
CRASHTYPE(EXEC_RODATA),
|
||||||
"ATOMIC_OVERFLOW",
|
CRASHTYPE(EXEC_USERSPACE),
|
||||||
"USERCOPY_HEAP_SIZE_TO",
|
CRASHTYPE(ACCESS_USERSPACE),
|
||||||
"USERCOPY_HEAP_SIZE_FROM",
|
CRASHTYPE(WRITE_RO),
|
||||||
"USERCOPY_HEAP_FLAG_TO",
|
CRASHTYPE(WRITE_RO_AFTER_INIT),
|
||||||
"USERCOPY_HEAP_FLAG_FROM",
|
CRASHTYPE(WRITE_KERN),
|
||||||
"USERCOPY_STACK_FRAME_TO",
|
CRASHTYPE(ATOMIC_UNDERFLOW),
|
||||||
"USERCOPY_STACK_FRAME_FROM",
|
CRASHTYPE(ATOMIC_OVERFLOW),
|
||||||
"USERCOPY_STACK_BEYOND",
|
CRASHTYPE(USERCOPY_HEAP_SIZE_TO),
|
||||||
"USERCOPY_KERNEL",
|
CRASHTYPE(USERCOPY_HEAP_SIZE_FROM),
|
||||||
|
CRASHTYPE(USERCOPY_HEAP_FLAG_TO),
|
||||||
|
CRASHTYPE(USERCOPY_HEAP_FLAG_FROM),
|
||||||
|
CRASHTYPE(USERCOPY_STACK_FRAME_TO),
|
||||||
|
CRASHTYPE(USERCOPY_STACK_FRAME_FROM),
|
||||||
|
CRASHTYPE(USERCOPY_STACK_BEYOND),
|
||||||
|
CRASHTYPE(USERCOPY_KERNEL),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/* Global jprobe entry and crashtype. */
|
/* Global jprobe entry and crashtype. */
|
||||||
static struct jprobe lkdtm_jprobe;
|
static struct jprobe *lkdtm_jprobe;
|
||||||
static enum cname lkdtm_crashpoint = CN_INVALID;
|
struct crashpoint *lkdtm_crashpoint;
|
||||||
static enum ctype lkdtm_crashtype = CT_NONE;
|
struct crashtype *lkdtm_crashtype;
|
||||||
|
|
||||||
/* Global crash counter and spinlock. */
|
/* Global crash counter and spinlock. */
|
||||||
static int crash_count = DEFAULT_COUNT;
|
static int crash_count = DEFAULT_COUNT;
|
||||||
|
@ -257,206 +261,42 @@ module_param(cpoint_count, int, 0644);
|
||||||
MODULE_PARM_DESC(cpoint_count, " Crash Point Count, number of times the "\
|
MODULE_PARM_DESC(cpoint_count, " Crash Point Count, number of times the "\
|
||||||
"crash point is to be hit to trigger action");
|
"crash point is to be hit to trigger action");
|
||||||
|
|
||||||
/* Return the crashpoint number or NONE if the name is invalid */
|
|
||||||
static enum ctype parse_cp_type(const char *what, size_t count)
|
/* Return the crashtype number or NULL if the name is invalid */
|
||||||
|
static struct crashtype *find_crashtype(const char *name)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(cp_type); i++) {
|
for (i = 0; i < ARRAY_SIZE(crashtypes); i++) {
|
||||||
if (!strcmp(what, cp_type[i]))
|
if (!strcmp(name, crashtypes[i].name))
|
||||||
return i;
|
return &crashtypes[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
return CT_NONE;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *cp_type_to_str(enum ctype type)
|
/*
|
||||||
|
* This is forced noinline just so it distinctly shows up in the stackdump
|
||||||
|
* which makes validation of expected lkdtm crashes easier.
|
||||||
|
*/
|
||||||
|
static noinline void lkdtm_do_action(struct crashtype *crashtype)
|
||||||
{
|
{
|
||||||
if (type == CT_NONE || type < 0 || type > ARRAY_SIZE(cp_type))
|
BUG_ON(!crashtype || !crashtype->func);
|
||||||
return "NONE";
|
crashtype->func();
|
||||||
|
|
||||||
return cp_type[type];
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *cp_name_to_str(enum cname name)
|
|
||||||
{
|
|
||||||
if (name == CN_INVALID || name < 0 || name > ARRAY_SIZE(cp_name))
|
|
||||||
return "INVALID";
|
|
||||||
|
|
||||||
return cp_name[name];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int lkdtm_parse_commandline(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
if (cpoint_count < 1 || recur_count < 1)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&crash_count_lock, flags);
|
|
||||||
crash_count = cpoint_count;
|
|
||||||
spin_unlock_irqrestore(&crash_count_lock, flags);
|
|
||||||
|
|
||||||
/* No special parameters */
|
|
||||||
if (!cpoint_type && !cpoint_name)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Neither or both of these need to be set */
|
|
||||||
if (!cpoint_type || !cpoint_name)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
lkdtm_crashtype = parse_cp_type(cpoint_type, strlen(cpoint_type));
|
|
||||||
if (lkdtm_crashtype == CT_NONE)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
/* Refuse INVALID as a selectable crashpoint name. */
|
|
||||||
if (!strcmp(cpoint_name, "INVALID"))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(cp_name); i++) {
|
|
||||||
if (!strcmp(cpoint_name, cp_name[i])) {
|
|
||||||
lkdtm_crashpoint = i;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Could not find a valid crash point */
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void lkdtm_do_action(enum ctype which)
|
|
||||||
{
|
|
||||||
switch (which) {
|
|
||||||
case CT_PANIC:
|
|
||||||
lkdtm_PANIC();
|
|
||||||
break;
|
|
||||||
case CT_BUG:
|
|
||||||
lkdtm_BUG();
|
|
||||||
break;
|
|
||||||
case CT_WARNING:
|
|
||||||
lkdtm_WARNING();
|
|
||||||
break;
|
|
||||||
case CT_EXCEPTION:
|
|
||||||
lkdtm_EXCEPTION();
|
|
||||||
break;
|
|
||||||
case CT_LOOP:
|
|
||||||
lkdtm_LOOP();
|
|
||||||
break;
|
|
||||||
case CT_OVERFLOW:
|
|
||||||
lkdtm_OVERFLOW();
|
|
||||||
break;
|
|
||||||
case CT_CORRUPT_STACK:
|
|
||||||
lkdtm_CORRUPT_STACK();
|
|
||||||
break;
|
|
||||||
case CT_UNALIGNED_LOAD_STORE_WRITE:
|
|
||||||
lkdtm_UNALIGNED_LOAD_STORE_WRITE();
|
|
||||||
break;
|
|
||||||
case CT_OVERWRITE_ALLOCATION:
|
|
||||||
lkdtm_OVERWRITE_ALLOCATION();
|
|
||||||
break;
|
|
||||||
case CT_WRITE_AFTER_FREE:
|
|
||||||
lkdtm_WRITE_AFTER_FREE();
|
|
||||||
break;
|
|
||||||
case CT_READ_AFTER_FREE:
|
|
||||||
lkdtm_READ_AFTER_FREE();
|
|
||||||
break;
|
|
||||||
case CT_WRITE_BUDDY_AFTER_FREE:
|
|
||||||
lkdtm_WRITE_BUDDY_AFTER_FREE();
|
|
||||||
break;
|
|
||||||
case CT_READ_BUDDY_AFTER_FREE:
|
|
||||||
lkdtm_READ_BUDDY_AFTER_FREE();
|
|
||||||
break;
|
|
||||||
case CT_SOFTLOCKUP:
|
|
||||||
lkdtm_SOFTLOCKUP();
|
|
||||||
break;
|
|
||||||
case CT_HARDLOCKUP:
|
|
||||||
lkdtm_HARDLOCKUP();
|
|
||||||
break;
|
|
||||||
case CT_SPINLOCKUP:
|
|
||||||
lkdtm_SPINLOCKUP();
|
|
||||||
break;
|
|
||||||
case CT_HUNG_TASK:
|
|
||||||
lkdtm_HUNG_TASK();
|
|
||||||
break;
|
|
||||||
case CT_EXEC_DATA:
|
|
||||||
lkdtm_EXEC_DATA();
|
|
||||||
break;
|
|
||||||
case CT_EXEC_STACK:
|
|
||||||
lkdtm_EXEC_STACK();
|
|
||||||
break;
|
|
||||||
case CT_EXEC_KMALLOC:
|
|
||||||
lkdtm_EXEC_KMALLOC();
|
|
||||||
break;
|
|
||||||
case CT_EXEC_VMALLOC:
|
|
||||||
lkdtm_EXEC_VMALLOC();
|
|
||||||
break;
|
|
||||||
case CT_EXEC_RODATA:
|
|
||||||
lkdtm_EXEC_RODATA();
|
|
||||||
break;
|
|
||||||
case CT_EXEC_USERSPACE:
|
|
||||||
lkdtm_EXEC_USERSPACE();
|
|
||||||
break;
|
|
||||||
case CT_ACCESS_USERSPACE:
|
|
||||||
lkdtm_ACCESS_USERSPACE();
|
|
||||||
break;
|
|
||||||
case CT_WRITE_RO:
|
|
||||||
lkdtm_WRITE_RO();
|
|
||||||
break;
|
|
||||||
case CT_WRITE_RO_AFTER_INIT:
|
|
||||||
lkdtm_WRITE_RO_AFTER_INIT();
|
|
||||||
break;
|
|
||||||
case CT_WRITE_KERN:
|
|
||||||
lkdtm_WRITE_KERN();
|
|
||||||
break;
|
|
||||||
case CT_ATOMIC_UNDERFLOW:
|
|
||||||
lkdtm_ATOMIC_UNDERFLOW();
|
|
||||||
break;
|
|
||||||
case CT_ATOMIC_OVERFLOW:
|
|
||||||
lkdtm_ATOMIC_OVERFLOW();
|
|
||||||
break;
|
|
||||||
case CT_USERCOPY_HEAP_SIZE_TO:
|
|
||||||
lkdtm_USERCOPY_HEAP_SIZE_TO();
|
|
||||||
break;
|
|
||||||
case CT_USERCOPY_HEAP_SIZE_FROM:
|
|
||||||
lkdtm_USERCOPY_HEAP_SIZE_FROM();
|
|
||||||
break;
|
|
||||||
case CT_USERCOPY_HEAP_FLAG_TO:
|
|
||||||
lkdtm_USERCOPY_HEAP_FLAG_TO();
|
|
||||||
break;
|
|
||||||
case CT_USERCOPY_HEAP_FLAG_FROM:
|
|
||||||
lkdtm_USERCOPY_HEAP_FLAG_FROM();
|
|
||||||
break;
|
|
||||||
case CT_USERCOPY_STACK_FRAME_TO:
|
|
||||||
lkdtm_USERCOPY_STACK_FRAME_TO();
|
|
||||||
break;
|
|
||||||
case CT_USERCOPY_STACK_FRAME_FROM:
|
|
||||||
lkdtm_USERCOPY_STACK_FRAME_FROM();
|
|
||||||
break;
|
|
||||||
case CT_USERCOPY_STACK_BEYOND:
|
|
||||||
lkdtm_USERCOPY_STACK_BEYOND();
|
|
||||||
break;
|
|
||||||
case CT_USERCOPY_KERNEL:
|
|
||||||
lkdtm_USERCOPY_KERNEL();
|
|
||||||
break;
|
|
||||||
case CT_NONE:
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Called by jprobe entry points. */
|
||||||
static void lkdtm_handler(void)
|
static void lkdtm_handler(void)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
bool do_it = false;
|
bool do_it = false;
|
||||||
|
|
||||||
|
BUG_ON(!lkdtm_crashpoint || !lkdtm_crashtype);
|
||||||
|
|
||||||
spin_lock_irqsave(&crash_count_lock, flags);
|
spin_lock_irqsave(&crash_count_lock, flags);
|
||||||
crash_count--;
|
crash_count--;
|
||||||
pr_info("Crash point %s of type %s hit, trigger in %d rounds\n",
|
pr_info("Crash point %s of type %s hit, trigger in %d rounds\n",
|
||||||
cp_name_to_str(lkdtm_crashpoint),
|
lkdtm_crashpoint->name, lkdtm_crashtype->name, crash_count);
|
||||||
cp_type_to_str(lkdtm_crashtype), crash_count);
|
|
||||||
|
|
||||||
if (crash_count == 0) {
|
if (crash_count == 0) {
|
||||||
do_it = true;
|
do_it = true;
|
||||||
|
@ -468,72 +308,41 @@ static void lkdtm_handler(void)
|
||||||
lkdtm_do_action(lkdtm_crashtype);
|
lkdtm_do_action(lkdtm_crashtype);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int lkdtm_register_cpoint(enum cname which)
|
static int lkdtm_register_cpoint(struct crashpoint *crashpoint,
|
||||||
|
struct crashtype *crashtype)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
lkdtm_crashpoint = CN_INVALID;
|
/* If this doesn't have a symbol, just call immediately. */
|
||||||
if (lkdtm_jprobe.entry != NULL)
|
if (!crashpoint->jprobe.kp.symbol_name) {
|
||||||
unregister_jprobe(&lkdtm_jprobe);
|
lkdtm_do_action(crashtype);
|
||||||
|
|
||||||
switch (which) {
|
|
||||||
case CN_DIRECT:
|
|
||||||
lkdtm_do_action(lkdtm_crashtype);
|
|
||||||
return 0;
|
return 0;
|
||||||
case CN_INT_HARDWARE_ENTRY:
|
|
||||||
lkdtm_jprobe.kp.symbol_name = "do_IRQ";
|
|
||||||
lkdtm_jprobe.entry = (kprobe_opcode_t*) jp_do_irq;
|
|
||||||
break;
|
|
||||||
case CN_INT_HW_IRQ_EN:
|
|
||||||
lkdtm_jprobe.kp.symbol_name = "handle_IRQ_event";
|
|
||||||
lkdtm_jprobe.entry = (kprobe_opcode_t*) jp_handle_irq_event;
|
|
||||||
break;
|
|
||||||
case CN_INT_TASKLET_ENTRY:
|
|
||||||
lkdtm_jprobe.kp.symbol_name = "tasklet_action";
|
|
||||||
lkdtm_jprobe.entry = (kprobe_opcode_t*) jp_tasklet_action;
|
|
||||||
break;
|
|
||||||
case CN_FS_DEVRW:
|
|
||||||
lkdtm_jprobe.kp.symbol_name = "ll_rw_block";
|
|
||||||
lkdtm_jprobe.entry = (kprobe_opcode_t*) jp_ll_rw_block;
|
|
||||||
break;
|
|
||||||
case CN_MEM_SWAPOUT:
|
|
||||||
lkdtm_jprobe.kp.symbol_name = "shrink_inactive_list";
|
|
||||||
lkdtm_jprobe.entry = (kprobe_opcode_t*) jp_shrink_inactive_list;
|
|
||||||
break;
|
|
||||||
case CN_TIMERADD:
|
|
||||||
lkdtm_jprobe.kp.symbol_name = "hrtimer_start";
|
|
||||||
lkdtm_jprobe.entry = (kprobe_opcode_t*) jp_hrtimer_start;
|
|
||||||
break;
|
|
||||||
case CN_SCSI_DISPATCH_CMD:
|
|
||||||
lkdtm_jprobe.kp.symbol_name = "scsi_dispatch_cmd";
|
|
||||||
lkdtm_jprobe.entry = (kprobe_opcode_t*) jp_scsi_dispatch_cmd;
|
|
||||||
break;
|
|
||||||
case CN_IDE_CORE_CP:
|
|
||||||
#ifdef CONFIG_IDE
|
|
||||||
lkdtm_jprobe.kp.symbol_name = "generic_ide_ioctl";
|
|
||||||
lkdtm_jprobe.entry = (kprobe_opcode_t*) jp_generic_ide_ioctl;
|
|
||||||
#else
|
|
||||||
pr_info("Crash point not available\n");
|
|
||||||
return -EINVAL;
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
pr_info("Invalid Crash Point\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lkdtm_crashpoint = which;
|
if (lkdtm_jprobe != NULL)
|
||||||
if ((ret = register_jprobe(&lkdtm_jprobe)) < 0) {
|
unregister_jprobe(lkdtm_jprobe);
|
||||||
pr_info("Couldn't register jprobe\n");
|
|
||||||
lkdtm_crashpoint = CN_INVALID;
|
lkdtm_crashpoint = crashpoint;
|
||||||
|
lkdtm_crashtype = crashtype;
|
||||||
|
lkdtm_jprobe = &crashpoint->jprobe;
|
||||||
|
ret = register_jprobe(lkdtm_jprobe);
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_info("Couldn't register jprobe %s\n",
|
||||||
|
crashpoint->jprobe.kp.symbol_name);
|
||||||
|
lkdtm_jprobe = NULL;
|
||||||
|
lkdtm_crashpoint = NULL;
|
||||||
|
lkdtm_crashtype = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t do_register_entry(enum cname which, struct file *f,
|
static ssize_t lkdtm_debugfs_entry(struct file *f,
|
||||||
const char __user *user_buf, size_t count, loff_t *off)
|
const char __user *user_buf,
|
||||||
|
size_t count, loff_t *off)
|
||||||
{
|
{
|
||||||
|
struct crashpoint *crashpoint = file_inode(f)->i_private;
|
||||||
|
struct crashtype *crashtype = NULL;
|
||||||
char *buf;
|
char *buf;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
@ -551,13 +360,13 @@ static ssize_t do_register_entry(enum cname which, struct file *f,
|
||||||
buf[count] = '\0';
|
buf[count] = '\0';
|
||||||
strim(buf);
|
strim(buf);
|
||||||
|
|
||||||
lkdtm_crashtype = parse_cp_type(buf, count);
|
crashtype = find_crashtype(buf);
|
||||||
free_page((unsigned long) buf);
|
free_page((unsigned long)buf);
|
||||||
|
|
||||||
if (lkdtm_crashtype == CT_NONE)
|
if (!crashtype)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
err = lkdtm_register_cpoint(which);
|
err = lkdtm_register_cpoint(crashpoint, crashtype);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
@ -578,8 +387,10 @@ static ssize_t lkdtm_debugfs_read(struct file *f, char __user *user_buf,
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
n = snprintf(buf, PAGE_SIZE, "Available crash types:\n");
|
n = snprintf(buf, PAGE_SIZE, "Available crash types:\n");
|
||||||
for (i = 0; i < ARRAY_SIZE(cp_type); i++)
|
for (i = 0; i < ARRAY_SIZE(crashtypes); i++) {
|
||||||
n += snprintf(buf + n, PAGE_SIZE - n, "%s\n", cp_type[i]);
|
n += snprintf(buf + n, PAGE_SIZE - n, "%s\n",
|
||||||
|
crashtypes[i].name);
|
||||||
|
}
|
||||||
buf[n] = '\0';
|
buf[n] = '\0';
|
||||||
|
|
||||||
out = simple_read_from_buffer(user_buf, count, off,
|
out = simple_read_from_buffer(user_buf, count, off,
|
||||||
|
@ -594,60 +405,11 @@ static int lkdtm_debugfs_open(struct inode *inode, struct file *file)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static ssize_t int_hardware_entry(struct file *f, const char __user *buf,
|
|
||||||
size_t count, loff_t *off)
|
|
||||||
{
|
|
||||||
return do_register_entry(CN_INT_HARDWARE_ENTRY, f, buf, count, off);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t int_hw_irq_en(struct file *f, const char __user *buf,
|
|
||||||
size_t count, loff_t *off)
|
|
||||||
{
|
|
||||||
return do_register_entry(CN_INT_HW_IRQ_EN, f, buf, count, off);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t int_tasklet_entry(struct file *f, const char __user *buf,
|
|
||||||
size_t count, loff_t *off)
|
|
||||||
{
|
|
||||||
return do_register_entry(CN_INT_TASKLET_ENTRY, f, buf, count, off);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t fs_devrw_entry(struct file *f, const char __user *buf,
|
|
||||||
size_t count, loff_t *off)
|
|
||||||
{
|
|
||||||
return do_register_entry(CN_FS_DEVRW, f, buf, count, off);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t mem_swapout_entry(struct file *f, const char __user *buf,
|
|
||||||
size_t count, loff_t *off)
|
|
||||||
{
|
|
||||||
return do_register_entry(CN_MEM_SWAPOUT, f, buf, count, off);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t timeradd_entry(struct file *f, const char __user *buf,
|
|
||||||
size_t count, loff_t *off)
|
|
||||||
{
|
|
||||||
return do_register_entry(CN_TIMERADD, f, buf, count, off);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t scsi_dispatch_cmd_entry(struct file *f,
|
|
||||||
const char __user *buf, size_t count, loff_t *off)
|
|
||||||
{
|
|
||||||
return do_register_entry(CN_SCSI_DISPATCH_CMD, f, buf, count, off);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t ide_core_cp_entry(struct file *f, const char __user *buf,
|
|
||||||
size_t count, loff_t *off)
|
|
||||||
{
|
|
||||||
return do_register_entry(CN_IDE_CORE_CP, f, buf, count, off);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Special entry to just crash directly. Available without KPROBEs */
|
/* Special entry to just crash directly. Available without KPROBEs */
|
||||||
static ssize_t direct_entry(struct file *f, const char __user *user_buf,
|
static ssize_t direct_entry(struct file *f, const char __user *user_buf,
|
||||||
size_t count, loff_t *off)
|
size_t count, loff_t *off)
|
||||||
{
|
{
|
||||||
enum ctype type;
|
struct crashtype *crashtype;
|
||||||
char *buf;
|
char *buf;
|
||||||
|
|
||||||
if (count >= PAGE_SIZE)
|
if (count >= PAGE_SIZE)
|
||||||
|
@ -666,70 +428,57 @@ static ssize_t direct_entry(struct file *f, const char __user *user_buf,
|
||||||
buf[count] = '\0';
|
buf[count] = '\0';
|
||||||
strim(buf);
|
strim(buf);
|
||||||
|
|
||||||
type = parse_cp_type(buf, count);
|
crashtype = find_crashtype(buf);
|
||||||
free_page((unsigned long) buf);
|
free_page((unsigned long) buf);
|
||||||
if (type == CT_NONE)
|
if (!crashtype)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
pr_info("Performing direct entry %s\n", cp_type_to_str(type));
|
pr_info("Performing direct entry %s\n", crashtype->name);
|
||||||
lkdtm_do_action(type);
|
lkdtm_do_action(crashtype);
|
||||||
*off += count;
|
*off += count;
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct crash_entry {
|
|
||||||
const char *name;
|
|
||||||
const struct file_operations fops;
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct crash_entry crash_entries[] = {
|
|
||||||
{"DIRECT", {.read = lkdtm_debugfs_read,
|
|
||||||
.llseek = generic_file_llseek,
|
|
||||||
.open = lkdtm_debugfs_open,
|
|
||||||
.write = direct_entry} },
|
|
||||||
{"INT_HARDWARE_ENTRY", {.read = lkdtm_debugfs_read,
|
|
||||||
.llseek = generic_file_llseek,
|
|
||||||
.open = lkdtm_debugfs_open,
|
|
||||||
.write = int_hardware_entry} },
|
|
||||||
{"INT_HW_IRQ_EN", {.read = lkdtm_debugfs_read,
|
|
||||||
.llseek = generic_file_llseek,
|
|
||||||
.open = lkdtm_debugfs_open,
|
|
||||||
.write = int_hw_irq_en} },
|
|
||||||
{"INT_TASKLET_ENTRY", {.read = lkdtm_debugfs_read,
|
|
||||||
.llseek = generic_file_llseek,
|
|
||||||
.open = lkdtm_debugfs_open,
|
|
||||||
.write = int_tasklet_entry} },
|
|
||||||
{"FS_DEVRW", {.read = lkdtm_debugfs_read,
|
|
||||||
.llseek = generic_file_llseek,
|
|
||||||
.open = lkdtm_debugfs_open,
|
|
||||||
.write = fs_devrw_entry} },
|
|
||||||
{"MEM_SWAPOUT", {.read = lkdtm_debugfs_read,
|
|
||||||
.llseek = generic_file_llseek,
|
|
||||||
.open = lkdtm_debugfs_open,
|
|
||||||
.write = mem_swapout_entry} },
|
|
||||||
{"TIMERADD", {.read = lkdtm_debugfs_read,
|
|
||||||
.llseek = generic_file_llseek,
|
|
||||||
.open = lkdtm_debugfs_open,
|
|
||||||
.write = timeradd_entry} },
|
|
||||||
{"SCSI_DISPATCH_CMD", {.read = lkdtm_debugfs_read,
|
|
||||||
.llseek = generic_file_llseek,
|
|
||||||
.open = lkdtm_debugfs_open,
|
|
||||||
.write = scsi_dispatch_cmd_entry} },
|
|
||||||
{"IDE_CORE_CP", {.read = lkdtm_debugfs_read,
|
|
||||||
.llseek = generic_file_llseek,
|
|
||||||
.open = lkdtm_debugfs_open,
|
|
||||||
.write = ide_core_cp_entry} },
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct dentry *lkdtm_debugfs_root;
|
static struct dentry *lkdtm_debugfs_root;
|
||||||
|
|
||||||
static int __init lkdtm_module_init(void)
|
static int __init lkdtm_module_init(void)
|
||||||
{
|
{
|
||||||
|
struct crashpoint *crashpoint = NULL;
|
||||||
|
struct crashtype *crashtype = NULL;
|
||||||
int ret = -EINVAL;
|
int ret = -EINVAL;
|
||||||
int n_debugfs_entries = 1; /* Assume only the direct entry */
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
/* Neither or both of these need to be set */
|
||||||
|
if ((cpoint_type || cpoint_name) && !(cpoint_type && cpoint_name)) {
|
||||||
|
pr_err("Need both cpoint_type and cpoint_name or neither\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cpoint_type) {
|
||||||
|
crashtype = find_crashtype(cpoint_type);
|
||||||
|
if (!crashtype) {
|
||||||
|
pr_err("Unknown crashtype '%s'\n", cpoint_type);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cpoint_name) {
|
||||||
|
for (i = 0; i < ARRAY_SIZE(crashpoints); i++) {
|
||||||
|
if (!strcmp(cpoint_name, crashpoints[i].name))
|
||||||
|
crashpoint = &crashpoints[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Refuse unknown crashpoints. */
|
||||||
|
if (!crashpoint) {
|
||||||
|
pr_err("Invalid crashpoint %s\n", cpoint_name);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set crash count. */
|
||||||
|
crash_count = cpoint_count;
|
||||||
|
|
||||||
/* Handle test-specific initialization. */
|
/* Handle test-specific initialization. */
|
||||||
lkdtm_bugs_init(&recur_count);
|
lkdtm_bugs_init(&recur_count);
|
||||||
lkdtm_perms_init();
|
lkdtm_perms_init();
|
||||||
|
@ -742,35 +491,28 @@ static int __init lkdtm_module_init(void)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_KPROBES
|
/* Install debugfs trigger files. */
|
||||||
n_debugfs_entries = ARRAY_SIZE(crash_entries);
|
for (i = 0; i < ARRAY_SIZE(crashpoints); i++) {
|
||||||
#endif
|
struct crashpoint *cur = &crashpoints[i];
|
||||||
|
|
||||||
for (i = 0; i < n_debugfs_entries; i++) {
|
|
||||||
const struct crash_entry *cur = &crash_entries[i];
|
|
||||||
struct dentry *de;
|
struct dentry *de;
|
||||||
|
|
||||||
de = debugfs_create_file(cur->name, 0644, lkdtm_debugfs_root,
|
de = debugfs_create_file(cur->name, 0644, lkdtm_debugfs_root,
|
||||||
NULL, &cur->fops);
|
cur, &cur->fops);
|
||||||
if (de == NULL) {
|
if (de == NULL) {
|
||||||
pr_err("could not create %s\n", cur->name);
|
pr_err("could not create crashpoint %s\n", cur->name);
|
||||||
goto out_err;
|
goto out_err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lkdtm_parse_commandline() == -EINVAL) {
|
/* Install crashpoint if one was selected. */
|
||||||
pr_info("Invalid command\n");
|
if (crashpoint) {
|
||||||
goto out_err;
|
ret = lkdtm_register_cpoint(crashpoint, crashtype);
|
||||||
}
|
|
||||||
|
|
||||||
if (lkdtm_crashpoint != CN_INVALID && lkdtm_crashtype != CT_NONE) {
|
|
||||||
ret = lkdtm_register_cpoint(lkdtm_crashpoint);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
pr_info("Invalid crash point %d\n", lkdtm_crashpoint);
|
pr_info("Invalid crashpoint %s\n", crashpoint->name);
|
||||||
goto out_err;
|
goto out_err;
|
||||||
}
|
}
|
||||||
pr_info("Crash point %s of type %s registered\n",
|
pr_info("Crash point %s of type %s registered\n",
|
||||||
cpoint_name, cpoint_type);
|
crashpoint->name, cpoint_type);
|
||||||
} else {
|
} else {
|
||||||
pr_info("No crash points registered, enable through debugfs\n");
|
pr_info("No crash points registered, enable through debugfs\n");
|
||||||
}
|
}
|
||||||
|
@ -789,7 +531,7 @@ static void __exit lkdtm_module_exit(void)
|
||||||
/* Handle test-specific clean-up. */
|
/* Handle test-specific clean-up. */
|
||||||
lkdtm_usercopy_exit();
|
lkdtm_usercopy_exit();
|
||||||
|
|
||||||
unregister_jprobe(&lkdtm_jprobe);
|
unregister_jprobe(lkdtm_jprobe);
|
||||||
pr_info("Crash point unregistered\n");
|
pr_info("Crash point unregistered\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -797,4 +539,4 @@ module_init(lkdtm_module_init);
|
||||||
module_exit(lkdtm_module_exit);
|
module_exit(lkdtm_module_exit);
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_DESCRIPTION("Kprobe module for testing crash dumps");
|
MODULE_DESCRIPTION("Kernel crash testing module");
|
||||||
|
|
Loading…
Reference in New Issue