Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/misc/lkdtm.c
15109 views
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 <[email protected]>
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 <[email protected]>
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 <linux/slab.h>
44
#include <scsi/scsi_cmnd.h>
45
#include <linux/debugfs.h>
46
47
#ifdef CONFIG_IDE
48
#include <linux/ide.h>
49
#endif
50
51
#define DEFAULT_COUNT 10
52
#define REC_NUM_DEFAULT 10
53
54
enum cname {
55
CN_INVALID,
56
CN_INT_HARDWARE_ENTRY,
57
CN_INT_HW_IRQ_EN,
58
CN_INT_TASKLET_ENTRY,
59
CN_FS_DEVRW,
60
CN_MEM_SWAPOUT,
61
CN_TIMERADD,
62
CN_SCSI_DISPATCH_CMD,
63
CN_IDE_CORE_CP,
64
CN_DIRECT,
65
};
66
67
enum ctype {
68
CT_NONE,
69
CT_PANIC,
70
CT_BUG,
71
CT_EXCEPTION,
72
CT_LOOP,
73
CT_OVERFLOW,
74
CT_CORRUPT_STACK,
75
CT_UNALIGNED_LOAD_STORE_WRITE,
76
CT_OVERWRITE_ALLOCATION,
77
CT_WRITE_AFTER_FREE,
78
CT_SOFTLOCKUP,
79
CT_HARDLOCKUP,
80
CT_HUNG_TASK,
81
};
82
83
static char* cp_name[] = {
84
"INT_HARDWARE_ENTRY",
85
"INT_HW_IRQ_EN",
86
"INT_TASKLET_ENTRY",
87
"FS_DEVRW",
88
"MEM_SWAPOUT",
89
"TIMERADD",
90
"SCSI_DISPATCH_CMD",
91
"IDE_CORE_CP",
92
"DIRECT",
93
};
94
95
static char* cp_type[] = {
96
"PANIC",
97
"BUG",
98
"EXCEPTION",
99
"LOOP",
100
"OVERFLOW",
101
"CORRUPT_STACK",
102
"UNALIGNED_LOAD_STORE_WRITE",
103
"OVERWRITE_ALLOCATION",
104
"WRITE_AFTER_FREE",
105
"SOFTLOCKUP",
106
"HARDLOCKUP",
107
"HUNG_TASK",
108
};
109
110
static struct jprobe lkdtm;
111
112
static int lkdtm_parse_commandline(void);
113
static void lkdtm_handler(void);
114
115
static char* cpoint_name;
116
static char* cpoint_type;
117
static int cpoint_count = DEFAULT_COUNT;
118
static int recur_count = REC_NUM_DEFAULT;
119
120
static enum cname cpoint = CN_INVALID;
121
static enum ctype cptype = CT_NONE;
122
static int count = DEFAULT_COUNT;
123
static DEFINE_SPINLOCK(count_lock);
124
125
module_param(recur_count, int, 0644);
126
MODULE_PARM_DESC(recur_count, " Recursion level for the stack overflow test, "\
127
"default is 10");
128
module_param(cpoint_name, charp, 0444);
129
MODULE_PARM_DESC(cpoint_name, " Crash Point, where kernel is to be crashed");
130
module_param(cpoint_type, charp, 0444);
131
MODULE_PARM_DESC(cpoint_type, " Crash Point Type, action to be taken on "\
132
"hitting the crash point");
133
module_param(cpoint_count, int, 0644);
134
MODULE_PARM_DESC(cpoint_count, " Crash Point Count, number of times the "\
135
"crash point is to be hit to trigger action");
136
137
static unsigned int jp_do_irq(unsigned int irq)
138
{
139
lkdtm_handler();
140
jprobe_return();
141
return 0;
142
}
143
144
static irqreturn_t jp_handle_irq_event(unsigned int irq,
145
struct irqaction *action)
146
{
147
lkdtm_handler();
148
jprobe_return();
149
return 0;
150
}
151
152
static void jp_tasklet_action(struct softirq_action *a)
153
{
154
lkdtm_handler();
155
jprobe_return();
156
}
157
158
static void jp_ll_rw_block(int rw, int nr, struct buffer_head *bhs[])
159
{
160
lkdtm_handler();
161
jprobe_return();
162
}
163
164
struct scan_control;
165
166
static unsigned long jp_shrink_inactive_list(unsigned long max_scan,
167
struct zone *zone,
168
struct scan_control *sc)
169
{
170
lkdtm_handler();
171
jprobe_return();
172
return 0;
173
}
174
175
static int jp_hrtimer_start(struct hrtimer *timer, ktime_t tim,
176
const enum hrtimer_mode mode)
177
{
178
lkdtm_handler();
179
jprobe_return();
180
return 0;
181
}
182
183
static int jp_scsi_dispatch_cmd(struct scsi_cmnd *cmd)
184
{
185
lkdtm_handler();
186
jprobe_return();
187
return 0;
188
}
189
190
#ifdef CONFIG_IDE
191
int jp_generic_ide_ioctl(ide_drive_t *drive, struct file *file,
192
struct block_device *bdev, unsigned int cmd,
193
unsigned long arg)
194
{
195
lkdtm_handler();
196
jprobe_return();
197
return 0;
198
}
199
#endif
200
201
/* Return the crashpoint number or NONE if the name is invalid */
202
static enum ctype parse_cp_type(const char *what, size_t count)
203
{
204
int i;
205
206
for (i = 0; i < ARRAY_SIZE(cp_type); i++) {
207
if (!strcmp(what, cp_type[i]))
208
return i + 1;
209
}
210
211
return CT_NONE;
212
}
213
214
static const char *cp_type_to_str(enum ctype type)
215
{
216
if (type == CT_NONE || type < 0 || type > ARRAY_SIZE(cp_type))
217
return "None";
218
219
return cp_type[type - 1];
220
}
221
222
static const char *cp_name_to_str(enum cname name)
223
{
224
if (name == CN_INVALID || name < 0 || name > ARRAY_SIZE(cp_name))
225
return "INVALID";
226
227
return cp_name[name - 1];
228
}
229
230
231
static int lkdtm_parse_commandline(void)
232
{
233
int i;
234
unsigned long flags;
235
236
if (cpoint_count < 1 || recur_count < 1)
237
return -EINVAL;
238
239
spin_lock_irqsave(&count_lock, flags);
240
count = cpoint_count;
241
spin_unlock_irqrestore(&count_lock, flags);
242
243
/* No special parameters */
244
if (!cpoint_type && !cpoint_name)
245
return 0;
246
247
/* Neither or both of these need to be set */
248
if (!cpoint_type || !cpoint_name)
249
return -EINVAL;
250
251
cptype = parse_cp_type(cpoint_type, strlen(cpoint_type));
252
if (cptype == CT_NONE)
253
return -EINVAL;
254
255
for (i = 0; i < ARRAY_SIZE(cp_name); i++) {
256
if (!strcmp(cpoint_name, cp_name[i])) {
257
cpoint = i + 1;
258
return 0;
259
}
260
}
261
262
/* Could not find a valid crash point */
263
return -EINVAL;
264
}
265
266
static int recursive_loop(int a)
267
{
268
char buf[1024];
269
270
memset(buf,0xFF,1024);
271
recur_count--;
272
if (!recur_count)
273
return 0;
274
else
275
return recursive_loop(a);
276
}
277
278
static void lkdtm_do_action(enum ctype which)
279
{
280
switch (which) {
281
case CT_PANIC:
282
panic("dumptest");
283
break;
284
case CT_BUG:
285
BUG();
286
break;
287
case CT_EXCEPTION:
288
*((int *) 0) = 0;
289
break;
290
case CT_LOOP:
291
for (;;)
292
;
293
break;
294
case CT_OVERFLOW:
295
(void) recursive_loop(0);
296
break;
297
case CT_CORRUPT_STACK: {
298
volatile u32 data[8];
299
volatile u32 *p = data;
300
301
p[12] = 0x12345678;
302
break;
303
}
304
case CT_UNALIGNED_LOAD_STORE_WRITE: {
305
static u8 data[5] __attribute__((aligned(4))) = {1, 2,
306
3, 4, 5};
307
u32 *p;
308
u32 val = 0x12345678;
309
310
p = (u32 *)(data + 1);
311
if (*p == 0)
312
val = 0x87654321;
313
*p = val;
314
break;
315
}
316
case CT_OVERWRITE_ALLOCATION: {
317
size_t len = 1020;
318
u32 *data = kmalloc(len, GFP_KERNEL);
319
320
data[1024 / sizeof(u32)] = 0x12345678;
321
kfree(data);
322
break;
323
}
324
case CT_WRITE_AFTER_FREE: {
325
size_t len = 1024;
326
u32 *data = kmalloc(len, GFP_KERNEL);
327
328
kfree(data);
329
schedule();
330
memset(data, 0x78, len);
331
break;
332
}
333
case CT_SOFTLOCKUP:
334
preempt_disable();
335
for (;;)
336
cpu_relax();
337
break;
338
case CT_HARDLOCKUP:
339
local_irq_disable();
340
for (;;)
341
cpu_relax();
342
break;
343
case CT_HUNG_TASK:
344
set_current_state(TASK_UNINTERRUPTIBLE);
345
schedule();
346
break;
347
case CT_NONE:
348
default:
349
break;
350
}
351
352
}
353
354
static void lkdtm_handler(void)
355
{
356
unsigned long flags;
357
358
spin_lock_irqsave(&count_lock, flags);
359
count--;
360
printk(KERN_INFO "lkdtm: Crash point %s of type %s hit, trigger in %d rounds\n",
361
cp_name_to_str(cpoint), cp_type_to_str(cptype), count);
362
363
if (count == 0) {
364
lkdtm_do_action(cptype);
365
count = cpoint_count;
366
}
367
spin_unlock_irqrestore(&count_lock, flags);
368
}
369
370
static int lkdtm_register_cpoint(enum cname which)
371
{
372
int ret;
373
374
cpoint = CN_INVALID;
375
if (lkdtm.entry != NULL)
376
unregister_jprobe(&lkdtm);
377
378
switch (which) {
379
case CN_DIRECT:
380
lkdtm_do_action(cptype);
381
return 0;
382
case CN_INT_HARDWARE_ENTRY:
383
lkdtm.kp.symbol_name = "do_IRQ";
384
lkdtm.entry = (kprobe_opcode_t*) jp_do_irq;
385
break;
386
case CN_INT_HW_IRQ_EN:
387
lkdtm.kp.symbol_name = "handle_IRQ_event";
388
lkdtm.entry = (kprobe_opcode_t*) jp_handle_irq_event;
389
break;
390
case CN_INT_TASKLET_ENTRY:
391
lkdtm.kp.symbol_name = "tasklet_action";
392
lkdtm.entry = (kprobe_opcode_t*) jp_tasklet_action;
393
break;
394
case CN_FS_DEVRW:
395
lkdtm.kp.symbol_name = "ll_rw_block";
396
lkdtm.entry = (kprobe_opcode_t*) jp_ll_rw_block;
397
break;
398
case CN_MEM_SWAPOUT:
399
lkdtm.kp.symbol_name = "shrink_inactive_list";
400
lkdtm.entry = (kprobe_opcode_t*) jp_shrink_inactive_list;
401
break;
402
case CN_TIMERADD:
403
lkdtm.kp.symbol_name = "hrtimer_start";
404
lkdtm.entry = (kprobe_opcode_t*) jp_hrtimer_start;
405
break;
406
case CN_SCSI_DISPATCH_CMD:
407
lkdtm.kp.symbol_name = "scsi_dispatch_cmd";
408
lkdtm.entry = (kprobe_opcode_t*) jp_scsi_dispatch_cmd;
409
break;
410
case CN_IDE_CORE_CP:
411
#ifdef CONFIG_IDE
412
lkdtm.kp.symbol_name = "generic_ide_ioctl";
413
lkdtm.entry = (kprobe_opcode_t*) jp_generic_ide_ioctl;
414
#else
415
printk(KERN_INFO "lkdtm: Crash point not available\n");
416
return -EINVAL;
417
#endif
418
break;
419
default:
420
printk(KERN_INFO "lkdtm: Invalid Crash Point\n");
421
return -EINVAL;
422
}
423
424
cpoint = which;
425
if ((ret = register_jprobe(&lkdtm)) < 0) {
426
printk(KERN_INFO "lkdtm: Couldn't register jprobe\n");
427
cpoint = CN_INVALID;
428
}
429
430
return ret;
431
}
432
433
static ssize_t do_register_entry(enum cname which, struct file *f,
434
const char __user *user_buf, size_t count, loff_t *off)
435
{
436
char *buf;
437
int err;
438
439
if (count >= PAGE_SIZE)
440
return -EINVAL;
441
442
buf = (char *)__get_free_page(GFP_KERNEL);
443
if (!buf)
444
return -ENOMEM;
445
if (copy_from_user(buf, user_buf, count)) {
446
free_page((unsigned long) buf);
447
return -EFAULT;
448
}
449
/* NULL-terminate and remove enter */
450
buf[count] = '\0';
451
strim(buf);
452
453
cptype = parse_cp_type(buf, count);
454
free_page((unsigned long) buf);
455
456
if (cptype == CT_NONE)
457
return -EINVAL;
458
459
err = lkdtm_register_cpoint(which);
460
if (err < 0)
461
return err;
462
463
*off += count;
464
465
return count;
466
}
467
468
/* Generic read callback that just prints out the available crash types */
469
static ssize_t lkdtm_debugfs_read(struct file *f, char __user *user_buf,
470
size_t count, loff_t *off)
471
{
472
char *buf;
473
int i, n, out;
474
475
buf = (char *)__get_free_page(GFP_KERNEL);
476
477
n = snprintf(buf, PAGE_SIZE, "Available crash types:\n");
478
for (i = 0; i < ARRAY_SIZE(cp_type); i++)
479
n += snprintf(buf + n, PAGE_SIZE - n, "%s\n", cp_type[i]);
480
buf[n] = '\0';
481
482
out = simple_read_from_buffer(user_buf, count, off,
483
buf, n);
484
free_page((unsigned long) buf);
485
486
return out;
487
}
488
489
static int lkdtm_debugfs_open(struct inode *inode, struct file *file)
490
{
491
return 0;
492
}
493
494
495
static ssize_t int_hardware_entry(struct file *f, const char __user *buf,
496
size_t count, loff_t *off)
497
{
498
return do_register_entry(CN_INT_HARDWARE_ENTRY, f, buf, count, off);
499
}
500
501
static ssize_t int_hw_irq_en(struct file *f, const char __user *buf,
502
size_t count, loff_t *off)
503
{
504
return do_register_entry(CN_INT_HW_IRQ_EN, f, buf, count, off);
505
}
506
507
static ssize_t int_tasklet_entry(struct file *f, const char __user *buf,
508
size_t count, loff_t *off)
509
{
510
return do_register_entry(CN_INT_TASKLET_ENTRY, f, buf, count, off);
511
}
512
513
static ssize_t fs_devrw_entry(struct file *f, const char __user *buf,
514
size_t count, loff_t *off)
515
{
516
return do_register_entry(CN_FS_DEVRW, f, buf, count, off);
517
}
518
519
static ssize_t mem_swapout_entry(struct file *f, const char __user *buf,
520
size_t count, loff_t *off)
521
{
522
return do_register_entry(CN_MEM_SWAPOUT, f, buf, count, off);
523
}
524
525
static ssize_t timeradd_entry(struct file *f, const char __user *buf,
526
size_t count, loff_t *off)
527
{
528
return do_register_entry(CN_TIMERADD, f, buf, count, off);
529
}
530
531
static ssize_t scsi_dispatch_cmd_entry(struct file *f,
532
const char __user *buf, size_t count, loff_t *off)
533
{
534
return do_register_entry(CN_SCSI_DISPATCH_CMD, f, buf, count, off);
535
}
536
537
static ssize_t ide_core_cp_entry(struct file *f, const char __user *buf,
538
size_t count, loff_t *off)
539
{
540
return do_register_entry(CN_IDE_CORE_CP, f, buf, count, off);
541
}
542
543
/* Special entry to just crash directly. Available without KPROBEs */
544
static ssize_t direct_entry(struct file *f, const char __user *user_buf,
545
size_t count, loff_t *off)
546
{
547
enum ctype type;
548
char *buf;
549
550
if (count >= PAGE_SIZE)
551
return -EINVAL;
552
if (count < 1)
553
return -EINVAL;
554
555
buf = (char *)__get_free_page(GFP_KERNEL);
556
if (!buf)
557
return -ENOMEM;
558
if (copy_from_user(buf, user_buf, count)) {
559
free_page((unsigned long) buf);
560
return -EFAULT;
561
}
562
/* NULL-terminate and remove enter */
563
buf[count] = '\0';
564
strim(buf);
565
566
type = parse_cp_type(buf, count);
567
free_page((unsigned long) buf);
568
if (type == CT_NONE)
569
return -EINVAL;
570
571
printk(KERN_INFO "lkdtm: Performing direct entry %s\n",
572
cp_type_to_str(type));
573
lkdtm_do_action(type);
574
*off += count;
575
576
return count;
577
}
578
579
struct crash_entry {
580
const char *name;
581
const struct file_operations fops;
582
};
583
584
static const struct crash_entry crash_entries[] = {
585
{"DIRECT", {.read = lkdtm_debugfs_read,
586
.llseek = generic_file_llseek,
587
.open = lkdtm_debugfs_open,
588
.write = direct_entry} },
589
{"INT_HARDWARE_ENTRY", {.read = lkdtm_debugfs_read,
590
.llseek = generic_file_llseek,
591
.open = lkdtm_debugfs_open,
592
.write = int_hardware_entry} },
593
{"INT_HW_IRQ_EN", {.read = lkdtm_debugfs_read,
594
.llseek = generic_file_llseek,
595
.open = lkdtm_debugfs_open,
596
.write = int_hw_irq_en} },
597
{"INT_TASKLET_ENTRY", {.read = lkdtm_debugfs_read,
598
.llseek = generic_file_llseek,
599
.open = lkdtm_debugfs_open,
600
.write = int_tasklet_entry} },
601
{"FS_DEVRW", {.read = lkdtm_debugfs_read,
602
.llseek = generic_file_llseek,
603
.open = lkdtm_debugfs_open,
604
.write = fs_devrw_entry} },
605
{"MEM_SWAPOUT", {.read = lkdtm_debugfs_read,
606
.llseek = generic_file_llseek,
607
.open = lkdtm_debugfs_open,
608
.write = mem_swapout_entry} },
609
{"TIMERADD", {.read = lkdtm_debugfs_read,
610
.llseek = generic_file_llseek,
611
.open = lkdtm_debugfs_open,
612
.write = timeradd_entry} },
613
{"SCSI_DISPATCH_CMD", {.read = lkdtm_debugfs_read,
614
.llseek = generic_file_llseek,
615
.open = lkdtm_debugfs_open,
616
.write = scsi_dispatch_cmd_entry} },
617
{"IDE_CORE_CP", {.read = lkdtm_debugfs_read,
618
.llseek = generic_file_llseek,
619
.open = lkdtm_debugfs_open,
620
.write = ide_core_cp_entry} },
621
};
622
623
static struct dentry *lkdtm_debugfs_root;
624
625
static int __init lkdtm_module_init(void)
626
{
627
int ret = -EINVAL;
628
int n_debugfs_entries = 1; /* Assume only the direct entry */
629
int i;
630
631
/* Register debugfs interface */
632
lkdtm_debugfs_root = debugfs_create_dir("provoke-crash", NULL);
633
if (!lkdtm_debugfs_root) {
634
printk(KERN_ERR "lkdtm: creating root dir failed\n");
635
return -ENODEV;
636
}
637
638
#ifdef CONFIG_KPROBES
639
n_debugfs_entries = ARRAY_SIZE(crash_entries);
640
#endif
641
642
for (i = 0; i < n_debugfs_entries; i++) {
643
const struct crash_entry *cur = &crash_entries[i];
644
struct dentry *de;
645
646
de = debugfs_create_file(cur->name, 0644, lkdtm_debugfs_root,
647
NULL, &cur->fops);
648
if (de == NULL) {
649
printk(KERN_ERR "lkdtm: could not create %s\n",
650
cur->name);
651
goto out_err;
652
}
653
}
654
655
if (lkdtm_parse_commandline() == -EINVAL) {
656
printk(KERN_INFO "lkdtm: Invalid command\n");
657
goto out_err;
658
}
659
660
if (cpoint != CN_INVALID && cptype != CT_NONE) {
661
ret = lkdtm_register_cpoint(cpoint);
662
if (ret < 0) {
663
printk(KERN_INFO "lkdtm: Invalid crash point %d\n",
664
cpoint);
665
goto out_err;
666
}
667
printk(KERN_INFO "lkdtm: Crash point %s of type %s registered\n",
668
cpoint_name, cpoint_type);
669
} else {
670
printk(KERN_INFO "lkdtm: No crash points registered, enable through debugfs\n");
671
}
672
673
return 0;
674
675
out_err:
676
debugfs_remove_recursive(lkdtm_debugfs_root);
677
return ret;
678
}
679
680
static void __exit lkdtm_module_exit(void)
681
{
682
debugfs_remove_recursive(lkdtm_debugfs_root);
683
684
unregister_jprobe(&lkdtm);
685
printk(KERN_INFO "lkdtm: Crash point unregistered\n");
686
}
687
688
module_init(lkdtm_module_init);
689
module_exit(lkdtm_module_exit);
690
691
MODULE_LICENSE("GPL");
692
693