lkdtm.c revision 0347af4ee3922220f6bfe74b87b526aa709a0365
1/* 2 * Kprobe module for testing crash dumps 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 * 18 * Copyright (C) IBM Corporation, 2006 19 * 20 * Author: Ankita Garg <ankita@in.ibm.com> 21 * 22 * This module induces system failures at predefined crashpoints to 23 * evaluate the reliability of crash dumps obtained using different dumping 24 * solutions. 25 * 26 * It is adapted from the Linux Kernel Dump Test Tool by 27 * Fernando Luis Vazquez Cao <http://lkdtt.sourceforge.net> 28 * 29 * Debugfs support added by Simon Kagstrom <simon.kagstrom@netinsight.net> 30 * 31 * See Documentation/fault-injection/provoke-crashes.txt for instructions 32 */ 33 34#include <linux/kernel.h> 35#include <linux/fs.h> 36#include <linux/module.h> 37#include <linux/buffer_head.h> 38#include <linux/kprobes.h> 39#include <linux/list.h> 40#include <linux/init.h> 41#include <linux/interrupt.h> 42#include <linux/hrtimer.h> 43#include <scsi/scsi_cmnd.h> 44#include <linux/debugfs.h> 45 46#ifdef CONFIG_IDE 47#include <linux/ide.h> 48#endif 49 50#define DEFAULT_COUNT 10 51#define REC_NUM_DEFAULT 10 52 53enum cname { 54 INVALID, 55 INT_HARDWARE_ENTRY, 56 INT_HW_IRQ_EN, 57 INT_TASKLET_ENTRY, 58 FS_DEVRW, 59 MEM_SWAPOUT, 60 TIMERADD, 61 SCSI_DISPATCH_CMD, 62 IDE_CORE_CP, 63 DIRECT, 64}; 65 66enum ctype { 67 NONE, 68 PANIC, 69 BUG, 70 EXCEPTION, 71 LOOP, 72 OVERFLOW, 73 CORRUPT_STACK, 74 UNALIGNED_LOAD_STORE_WRITE, 75 OVERWRITE_ALLOCATION, 76 WRITE_AFTER_FREE, 77}; 78 79static char* cp_name[] = { 80 "INT_HARDWARE_ENTRY", 81 "INT_HW_IRQ_EN", 82 "INT_TASKLET_ENTRY", 83 "FS_DEVRW", 84 "MEM_SWAPOUT", 85 "TIMERADD", 86 "SCSI_DISPATCH_CMD", 87 "IDE_CORE_CP", 88 "DIRECT", 89}; 90 91static char* cp_type[] = { 92 "PANIC", 93 "BUG", 94 "EXCEPTION", 95 "LOOP", 96 "OVERFLOW", 97 "CORRUPT_STACK", 98 "UNALIGNED_LOAD_STORE_WRITE", 99 "OVERWRITE_ALLOCATION", 100 "WRITE_AFTER_FREE", 101}; 102 103static struct jprobe lkdtm; 104 105static int lkdtm_parse_commandline(void); 106static void lkdtm_handler(void); 107 108static char* cpoint_name; 109static char* cpoint_type; 110static int cpoint_count = DEFAULT_COUNT; 111static int recur_count = REC_NUM_DEFAULT; 112 113static enum cname cpoint = INVALID; 114static enum ctype cptype = NONE; 115static int count = DEFAULT_COUNT; 116 117module_param(recur_count, int, 0644); 118MODULE_PARM_DESC(recur_count, " Recursion level for the stack overflow test, "\ 119 "default is 10"); 120module_param(cpoint_name, charp, 0644); 121MODULE_PARM_DESC(cpoint_name, " Crash Point, where kernel is to be crashed"); 122module_param(cpoint_type, charp, 0644); 123MODULE_PARM_DESC(cpoint_type, " Crash Point Type, action to be taken on "\ 124 "hitting the crash point"); 125module_param(cpoint_count, int, 0644); 126MODULE_PARM_DESC(cpoint_count, " Crash Point Count, number of times the "\ 127 "crash point is to be hit to trigger action"); 128 129static unsigned int jp_do_irq(unsigned int irq) 130{ 131 lkdtm_handler(); 132 jprobe_return(); 133 return 0; 134} 135 136static irqreturn_t jp_handle_irq_event(unsigned int irq, 137 struct irqaction *action) 138{ 139 lkdtm_handler(); 140 jprobe_return(); 141 return 0; 142} 143 144static void jp_tasklet_action(struct softirq_action *a) 145{ 146 lkdtm_handler(); 147 jprobe_return(); 148} 149 150static void jp_ll_rw_block(int rw, int nr, struct buffer_head *bhs[]) 151{ 152 lkdtm_handler(); 153 jprobe_return(); 154} 155 156struct scan_control; 157 158static unsigned long jp_shrink_inactive_list(unsigned long max_scan, 159 struct zone *zone, 160 struct scan_control *sc) 161{ 162 lkdtm_handler(); 163 jprobe_return(); 164 return 0; 165} 166 167static int jp_hrtimer_start(struct hrtimer *timer, ktime_t tim, 168 const enum hrtimer_mode mode) 169{ 170 lkdtm_handler(); 171 jprobe_return(); 172 return 0; 173} 174 175static int jp_scsi_dispatch_cmd(struct scsi_cmnd *cmd) 176{ 177 lkdtm_handler(); 178 jprobe_return(); 179 return 0; 180} 181 182#ifdef CONFIG_IDE 183int jp_generic_ide_ioctl(ide_drive_t *drive, struct file *file, 184 struct block_device *bdev, unsigned int cmd, 185 unsigned long arg) 186{ 187 lkdtm_handler(); 188 jprobe_return(); 189 return 0; 190} 191#endif 192 193/* Return the crashpoint number or NONE if the name is invalid */ 194static enum ctype parse_cp_type(const char *what, size_t count) 195{ 196 int i; 197 198 for (i = 0; i < ARRAY_SIZE(cp_type); i++) { 199 if (!strcmp(what, cp_type[i])) 200 return i + 1; 201 } 202 203 return NONE; 204} 205 206static const char *cp_type_to_str(enum ctype type) 207{ 208 if (type == NONE || type < 0 || type > ARRAY_SIZE(cp_type)) 209 return "None"; 210 211 return cp_type[type - 1]; 212} 213 214static const char *cp_name_to_str(enum cname name) 215{ 216 if (name == INVALID || name < 0 || name > ARRAY_SIZE(cp_name)) 217 return "INVALID"; 218 219 return cp_name[name - 1]; 220} 221 222 223static int lkdtm_parse_commandline(void) 224{ 225 int i; 226 227 if (cpoint_count < 1 || recur_count < 1) 228 return -EINVAL; 229 230 count = cpoint_count; 231 232 /* No special parameters */ 233 if (!cpoint_type && !cpoint_name) 234 return 0; 235 236 /* Neither or both of these need to be set */ 237 if (!cpoint_type || !cpoint_name) 238 return -EINVAL; 239 240 cptype = parse_cp_type(cpoint_type, strlen(cpoint_type)); 241 if (cptype == NONE) 242 return -EINVAL; 243 244 for (i = 0; i < ARRAY_SIZE(cp_name); i++) { 245 if (!strcmp(cpoint_name, cp_name[i])) { 246 cpoint = i + 1; 247 return 0; 248 } 249 } 250 251 /* Could not find a valid crash point */ 252 return -EINVAL; 253} 254 255static int recursive_loop(int a) 256{ 257 char buf[1024]; 258 259 memset(buf,0xFF,1024); 260 recur_count--; 261 if (!recur_count) 262 return 0; 263 else 264 return recursive_loop(a); 265} 266 267static void lkdtm_do_action(enum ctype which) 268{ 269 switch (which) { 270 case PANIC: 271 panic("dumptest"); 272 break; 273 case BUG: 274 BUG(); 275 break; 276 case EXCEPTION: 277 *((int *) 0) = 0; 278 break; 279 case LOOP: 280 for (;;) 281 ; 282 break; 283 case OVERFLOW: 284 (void) recursive_loop(0); 285 break; 286 case CORRUPT_STACK: { 287 volatile u32 data[8]; 288 volatile u32 *p = data; 289 290 p[12] = 0x12345678; 291 break; 292 } 293 case UNALIGNED_LOAD_STORE_WRITE: { 294 static u8 data[5] __attribute__((aligned(4))) = {1, 2, 295 3, 4, 5}; 296 u32 *p; 297 u32 val = 0x12345678; 298 299 p = (u32 *)(data + 1); 300 if (*p == 0) 301 val = 0x87654321; 302 *p = val; 303 break; 304 } 305 case OVERWRITE_ALLOCATION: { 306 size_t len = 1020; 307 u32 *data = kmalloc(len, GFP_KERNEL); 308 309 data[1024 / sizeof(u32)] = 0x12345678; 310 kfree(data); 311 break; 312 } 313 case WRITE_AFTER_FREE: { 314 size_t len = 1024; 315 u32 *data = kmalloc(len, GFP_KERNEL); 316 317 kfree(data); 318 schedule(); 319 memset(data, 0x78, len); 320 break; 321 } 322 case NONE: 323 default: 324 break; 325 } 326 327} 328 329static void lkdtm_handler(void) 330{ 331 count--; 332 printk(KERN_INFO "lkdtm: Crash point %s of type %s hit, trigger in %d rounds\n", 333 cp_name_to_str(cpoint), cp_type_to_str(cptype), count); 334 335 if (count == 0) { 336 lkdtm_do_action(cptype); 337 count = cpoint_count; 338 } 339} 340 341static int lkdtm_register_cpoint(enum cname which) 342{ 343 int ret; 344 345 cpoint = INVALID; 346 if (lkdtm.entry != NULL) 347 unregister_jprobe(&lkdtm); 348 349 switch (which) { 350 case DIRECT: 351 lkdtm_do_action(cptype); 352 return 0; 353 case INT_HARDWARE_ENTRY: 354 lkdtm.kp.symbol_name = "do_IRQ"; 355 lkdtm.entry = (kprobe_opcode_t*) jp_do_irq; 356 break; 357 case INT_HW_IRQ_EN: 358 lkdtm.kp.symbol_name = "handle_IRQ_event"; 359 lkdtm.entry = (kprobe_opcode_t*) jp_handle_irq_event; 360 break; 361 case INT_TASKLET_ENTRY: 362 lkdtm.kp.symbol_name = "tasklet_action"; 363 lkdtm.entry = (kprobe_opcode_t*) jp_tasklet_action; 364 break; 365 case FS_DEVRW: 366 lkdtm.kp.symbol_name = "ll_rw_block"; 367 lkdtm.entry = (kprobe_opcode_t*) jp_ll_rw_block; 368 break; 369 case MEM_SWAPOUT: 370 lkdtm.kp.symbol_name = "shrink_inactive_list"; 371 lkdtm.entry = (kprobe_opcode_t*) jp_shrink_inactive_list; 372 break; 373 case TIMERADD: 374 lkdtm.kp.symbol_name = "hrtimer_start"; 375 lkdtm.entry = (kprobe_opcode_t*) jp_hrtimer_start; 376 break; 377 case SCSI_DISPATCH_CMD: 378 lkdtm.kp.symbol_name = "scsi_dispatch_cmd"; 379 lkdtm.entry = (kprobe_opcode_t*) jp_scsi_dispatch_cmd; 380 break; 381 case IDE_CORE_CP: 382#ifdef CONFIG_IDE 383 lkdtm.kp.symbol_name = "generic_ide_ioctl"; 384 lkdtm.entry = (kprobe_opcode_t*) jp_generic_ide_ioctl; 385#else 386 printk(KERN_INFO "lkdtm: Crash point not available\n"); 387 return -EINVAL; 388#endif 389 break; 390 default: 391 printk(KERN_INFO "lkdtm: Invalid Crash Point\n"); 392 return -EINVAL; 393 } 394 395 cpoint = which; 396 if ((ret = register_jprobe(&lkdtm)) < 0) { 397 printk(KERN_INFO "lkdtm: Couldn't register jprobe\n"); 398 cpoint = INVALID; 399 } 400 401 return ret; 402} 403 404static ssize_t do_register_entry(enum cname which, struct file *f, 405 const char __user *user_buf, size_t count, loff_t *off) 406{ 407 char *buf; 408 int err; 409 410 if (count >= PAGE_SIZE) 411 return -EINVAL; 412 413 buf = (char *)__get_free_page(GFP_KERNEL); 414 if (!buf) 415 return -ENOMEM; 416 if (copy_from_user(buf, user_buf, count)) { 417 free_page((unsigned long) buf); 418 return -EFAULT; 419 } 420 /* NULL-terminate and remove enter */ 421 buf[count] = '\0'; 422 strim(buf); 423 424 cptype = parse_cp_type(buf, count); 425 free_page((unsigned long) buf); 426 427 if (cptype == NONE) 428 return -EINVAL; 429 430 err = lkdtm_register_cpoint(which); 431 if (err < 0) 432 return err; 433 434 *off += count; 435 436 return count; 437} 438 439/* Generic read callback that just prints out the available crash types */ 440static ssize_t lkdtm_debugfs_read(struct file *f, char __user *user_buf, 441 size_t count, loff_t *off) 442{ 443 char *buf; 444 int i, n, out; 445 446 buf = (char *)__get_free_page(GFP_KERNEL); 447 448 n = snprintf(buf, PAGE_SIZE, "Available crash types:\n"); 449 for (i = 0; i < ARRAY_SIZE(cp_type); i++) 450 n += snprintf(buf + n, PAGE_SIZE - n, "%s\n", cp_type[i]); 451 buf[n] = '\0'; 452 453 out = simple_read_from_buffer(user_buf, count, off, 454 buf, n); 455 free_page((unsigned long) buf); 456 457 return out; 458} 459 460static int lkdtm_debugfs_open(struct inode *inode, struct file *file) 461{ 462 return 0; 463} 464 465 466static ssize_t int_hardware_entry(struct file *f, const char __user *buf, 467 size_t count, loff_t *off) 468{ 469 return do_register_entry(INT_HARDWARE_ENTRY, f, buf, count, off); 470} 471 472static ssize_t int_hw_irq_en(struct file *f, const char __user *buf, 473 size_t count, loff_t *off) 474{ 475 return do_register_entry(INT_HW_IRQ_EN, f, buf, count, off); 476} 477 478static ssize_t int_tasklet_entry(struct file *f, const char __user *buf, 479 size_t count, loff_t *off) 480{ 481 return do_register_entry(INT_TASKLET_ENTRY, f, buf, count, off); 482} 483 484static ssize_t fs_devrw_entry(struct file *f, const char __user *buf, 485 size_t count, loff_t *off) 486{ 487 return do_register_entry(FS_DEVRW, f, buf, count, off); 488} 489 490static ssize_t mem_swapout_entry(struct file *f, const char __user *buf, 491 size_t count, loff_t *off) 492{ 493 return do_register_entry(MEM_SWAPOUT, f, buf, count, off); 494} 495 496static ssize_t timeradd_entry(struct file *f, const char __user *buf, 497 size_t count, loff_t *off) 498{ 499 return do_register_entry(TIMERADD, f, buf, count, off); 500} 501 502static ssize_t scsi_dispatch_cmd_entry(struct file *f, 503 const char __user *buf, size_t count, loff_t *off) 504{ 505 return do_register_entry(SCSI_DISPATCH_CMD, f, buf, count, off); 506} 507 508static ssize_t ide_core_cp_entry(struct file *f, const char __user *buf, 509 size_t count, loff_t *off) 510{ 511 return do_register_entry(IDE_CORE_CP, f, buf, count, off); 512} 513 514/* Special entry to just crash directly. Available without KPROBEs */ 515static ssize_t direct_entry(struct file *f, const char __user *user_buf, 516 size_t count, loff_t *off) 517{ 518 enum ctype type; 519 char *buf; 520 521 if (count >= PAGE_SIZE) 522 return -EINVAL; 523 if (count < 1) 524 return -EINVAL; 525 526 buf = (char *)__get_free_page(GFP_KERNEL); 527 if (!buf) 528 return -ENOMEM; 529 if (copy_from_user(buf, user_buf, count)) { 530 free_page((unsigned long) buf); 531 return -EFAULT; 532 } 533 /* NULL-terminate and remove enter */ 534 buf[count] = '\0'; 535 strim(buf); 536 537 type = parse_cp_type(buf, count); 538 free_page((unsigned long) buf); 539 if (type == NONE) 540 return -EINVAL; 541 542 printk(KERN_INFO "lkdtm: Performing direct entry %s\n", 543 cp_type_to_str(type)); 544 lkdtm_do_action(type); 545 *off += count; 546 547 return count; 548} 549 550struct crash_entry { 551 const char *name; 552 const struct file_operations fops; 553}; 554 555static const struct crash_entry crash_entries[] = { 556 {"DIRECT", {.read = lkdtm_debugfs_read, 557 .open = lkdtm_debugfs_open, 558 .write = direct_entry} }, 559 {"INT_HARDWARE_ENTRY", {.read = lkdtm_debugfs_read, 560 .open = lkdtm_debugfs_open, 561 .write = int_hardware_entry} }, 562 {"INT_HW_IRQ_EN", {.read = lkdtm_debugfs_read, 563 .open = lkdtm_debugfs_open, 564 .write = int_hw_irq_en} }, 565 {"INT_TASKLET_ENTRY", {.read = lkdtm_debugfs_read, 566 .open = lkdtm_debugfs_open, 567 .write = int_tasklet_entry} }, 568 {"FS_DEVRW", {.read = lkdtm_debugfs_read, 569 .open = lkdtm_debugfs_open, 570 .write = fs_devrw_entry} }, 571 {"MEM_SWAPOUT", {.read = lkdtm_debugfs_read, 572 .open = lkdtm_debugfs_open, 573 .write = mem_swapout_entry} }, 574 {"TIMERADD", {.read = lkdtm_debugfs_read, 575 .open = lkdtm_debugfs_open, 576 .write = timeradd_entry} }, 577 {"SCSI_DISPATCH_CMD", {.read = lkdtm_debugfs_read, 578 .open = lkdtm_debugfs_open, 579 .write = scsi_dispatch_cmd_entry} }, 580 {"IDE_CORE_CP", {.read = lkdtm_debugfs_read, 581 .open = lkdtm_debugfs_open, 582 .write = ide_core_cp_entry} }, 583}; 584 585static struct dentry *lkdtm_debugfs_root; 586 587static int __init lkdtm_module_init(void) 588{ 589 int ret = -EINVAL; 590 int n_debugfs_entries = 1; /* Assume only the direct entry */ 591 int i; 592 593 /* Register debugfs interface */ 594 lkdtm_debugfs_root = debugfs_create_dir("provoke-crash", NULL); 595 if (!lkdtm_debugfs_root) { 596 printk(KERN_ERR "lkdtm: creating root dir failed\n"); 597 return -ENODEV; 598 } 599 600#ifdef CONFIG_KPROBES 601 n_debugfs_entries = ARRAY_SIZE(crash_entries); 602#endif 603 604 for (i = 0; i < n_debugfs_entries; i++) { 605 const struct crash_entry *cur = &crash_entries[i]; 606 struct dentry *de; 607 608 de = debugfs_create_file(cur->name, 0644, lkdtm_debugfs_root, 609 NULL, &cur->fops); 610 if (de == NULL) { 611 printk(KERN_ERR "lkdtm: could not create %s\n", 612 cur->name); 613 goto out_err; 614 } 615 } 616 617 if (lkdtm_parse_commandline() == -EINVAL) { 618 printk(KERN_INFO "lkdtm: Invalid command\n"); 619 goto out_err; 620 } 621 622 if (cpoint != INVALID && cptype != NONE) { 623 ret = lkdtm_register_cpoint(cpoint); 624 if (ret < 0) { 625 printk(KERN_INFO "lkdtm: Invalid crash point %d\n", 626 cpoint); 627 goto out_err; 628 } 629 printk(KERN_INFO "lkdtm: Crash point %s of type %s registered\n", 630 cpoint_name, cpoint_type); 631 } else { 632 printk(KERN_INFO "lkdtm: No crash points registered, enable through debugfs\n"); 633 } 634 635 return 0; 636 637out_err: 638 debugfs_remove_recursive(lkdtm_debugfs_root); 639 return ret; 640} 641 642static void __exit lkdtm_module_exit(void) 643{ 644 debugfs_remove_recursive(lkdtm_debugfs_root); 645 646 unregister_jprobe(&lkdtm); 647 printk(KERN_INFO "lkdtm: Crash point unregistered\n"); 648} 649 650module_init(lkdtm_module_init); 651module_exit(lkdtm_module_exit); 652 653MODULE_LICENSE("GPL"); 654