lkdtm_core.c 19.2 KB
Newer Older
1
/*
K
Kees Cook 已提交
2 3 4 5 6
 * Linux Kernel Dump Test Module for testing kernel crashes conditions:
 * induces system failures at predefined crashpoints and under predefined
 * operational conditions in order to evaluate the reliability of kernel
 * sanity checking and crash dumps obtained using different dumping
 * solutions.
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * Copyright (C) IBM Corporation, 2006
 *
 * Author: Ankita Garg <ankita@in.ibm.com>
 *
 * It is adapted from the Linux Kernel Dump Test Tool by
 * Fernando Luis Vazquez Cao <http://lkdtt.sourceforge.net>
 *
29
 * Debugfs support added by Simon Kagstrom <simon.kagstrom@netinsight.net>
30
 *
31
 * See Documentation/fault-injection/provoke-crashes.txt for instructions
32
 */
K
Kees Cook 已提交
33
#define pr_fmt(fmt) "lkdtm: " fmt
34 35

#include <linux/kernel.h>
36
#include <linux/fs.h>
37
#include <linux/module.h>
38
#include <linux/buffer_head.h>
39
#include <linux/kprobes.h>
40
#include <linux/list.h>
41 42
#include <linux/init.h>
#include <linux/interrupt.h>
43
#include <linux/hrtimer.h>
44
#include <linux/slab.h>
45
#include <scsi/scsi_cmnd.h>
46
#include <linux/debugfs.h>
47 48 49 50 51

#ifdef CONFIG_IDE
#include <linux/ide.h>
#endif

52 53
#include "lkdtm.h"

54 55 56 57 58 59 60 61 62 63 64 65
/*
 * Make sure our attempts to over run the kernel stack doesn't trigger
 * a compiler warning when CONFIG_FRAME_WARN is set. Then make sure we
 * recurse past the end of THREAD_SIZE by default.
 */
#if defined(CONFIG_FRAME_WARN) && (CONFIG_FRAME_WARN > 0)
#define REC_STACK_SIZE (CONFIG_FRAME_WARN / 2)
#else
#define REC_STACK_SIZE (THREAD_SIZE / 8)
#endif
#define REC_NUM_DEFAULT ((THREAD_SIZE / REC_STACK_SIZE) * 2)

66 67 68
#define DEFAULT_COUNT 10

enum cname {
N
Namhyung Kim 已提交
69 70 71 72 73 74 75 76 77 78
	CN_INVALID,
	CN_INT_HARDWARE_ENTRY,
	CN_INT_HW_IRQ_EN,
	CN_INT_TASKLET_ENTRY,
	CN_FS_DEVRW,
	CN_MEM_SWAPOUT,
	CN_TIMERADD,
	CN_SCSI_DISPATCH_CMD,
	CN_IDE_CORE_CP,
	CN_DIRECT,
79 80 81
};

enum ctype {
N
Namhyung Kim 已提交
82 83 84
	CT_NONE,
	CT_PANIC,
	CT_BUG,
K
Kees Cook 已提交
85
	CT_WARNING,
N
Namhyung Kim 已提交
86 87 88 89 90 91 92
	CT_EXCEPTION,
	CT_LOOP,
	CT_OVERFLOW,
	CT_CORRUPT_STACK,
	CT_UNALIGNED_LOAD_STORE_WRITE,
	CT_OVERWRITE_ALLOCATION,
	CT_WRITE_AFTER_FREE,
L
Laura Abbott 已提交
93
	CT_READ_AFTER_FREE,
94 95
	CT_WRITE_BUDDY_AFTER_FREE,
	CT_READ_BUDDY_AFTER_FREE,
N
Namhyung Kim 已提交
96 97
	CT_SOFTLOCKUP,
	CT_HARDLOCKUP,
K
Kees Cook 已提交
98
	CT_SPINLOCKUP,
N
Namhyung Kim 已提交
99
	CT_HUNG_TASK,
K
Kees Cook 已提交
100 101 102 103
	CT_EXEC_DATA,
	CT_EXEC_STACK,
	CT_EXEC_KMALLOC,
	CT_EXEC_VMALLOC,
104
	CT_EXEC_RODATA,
105 106 107
	CT_EXEC_USERSPACE,
	CT_ACCESS_USERSPACE,
	CT_WRITE_RO,
108
	CT_WRITE_RO_AFTER_INIT,
K
Kees Cook 已提交
109
	CT_WRITE_KERN,
110 111
	CT_ATOMIC_UNDERFLOW,
	CT_ATOMIC_OVERFLOW,
K
Kees Cook 已提交
112 113 114 115 116 117 118
	CT_USERCOPY_HEAP_SIZE_TO,
	CT_USERCOPY_HEAP_SIZE_FROM,
	CT_USERCOPY_HEAP_FLAG_TO,
	CT_USERCOPY_HEAP_FLAG_FROM,
	CT_USERCOPY_STACK_FRAME_TO,
	CT_USERCOPY_STACK_FRAME_FROM,
	CT_USERCOPY_STACK_BEYOND,
119
	CT_USERCOPY_KERNEL,
120 121 122 123 124 125 126 127 128 129
};

static char* cp_name[] = {
	"INT_HARDWARE_ENTRY",
	"INT_HW_IRQ_EN",
	"INT_TASKLET_ENTRY",
	"FS_DEVRW",
	"MEM_SWAPOUT",
	"TIMERADD",
	"SCSI_DISPATCH_CMD",
130 131
	"IDE_CORE_CP",
	"DIRECT",
132 133 134 135 136
};

static char* cp_type[] = {
	"PANIC",
	"BUG",
K
Kees Cook 已提交
137
	"WARNING",
138 139
	"EXCEPTION",
	"LOOP",
140 141 142 143 144
	"OVERFLOW",
	"CORRUPT_STACK",
	"UNALIGNED_LOAD_STORE_WRITE",
	"OVERWRITE_ALLOCATION",
	"WRITE_AFTER_FREE",
L
Laura Abbott 已提交
145
	"READ_AFTER_FREE",
146 147
	"WRITE_BUDDY_AFTER_FREE",
	"READ_BUDDY_AFTER_FREE",
148 149
	"SOFTLOCKUP",
	"HARDLOCKUP",
K
Kees Cook 已提交
150
	"SPINLOCKUP",
151
	"HUNG_TASK",
K
Kees Cook 已提交
152 153 154 155
	"EXEC_DATA",
	"EXEC_STACK",
	"EXEC_KMALLOC",
	"EXEC_VMALLOC",
156
	"EXEC_RODATA",
157 158 159
	"EXEC_USERSPACE",
	"ACCESS_USERSPACE",
	"WRITE_RO",
160
	"WRITE_RO_AFTER_INIT",
K
Kees Cook 已提交
161
	"WRITE_KERN",
162 163
	"ATOMIC_UNDERFLOW",
	"ATOMIC_OVERFLOW",
K
Kees Cook 已提交
164 165 166 167 168 169 170
	"USERCOPY_HEAP_SIZE_TO",
	"USERCOPY_HEAP_SIZE_FROM",
	"USERCOPY_HEAP_FLAG_TO",
	"USERCOPY_HEAP_FLAG_FROM",
	"USERCOPY_STACK_FRAME_TO",
	"USERCOPY_STACK_FRAME_FROM",
	"USERCOPY_STACK_BEYOND",
171
	"USERCOPY_KERNEL",
172 173 174 175 176 177 178
};

static struct jprobe lkdtm;

static int lkdtm_parse_commandline(void);
static void lkdtm_handler(void);

179 180
static char* cpoint_name;
static char* cpoint_type;
181 182 183
static int cpoint_count = DEFAULT_COUNT;
static int recur_count = REC_NUM_DEFAULT;

N
Namhyung Kim 已提交
184 185
static enum cname cpoint = CN_INVALID;
static enum ctype cptype = CT_NONE;
186
static int count = DEFAULT_COUNT;
187
static DEFINE_SPINLOCK(count_lock);
K
Kees Cook 已提交
188
static DEFINE_SPINLOCK(lock_me_up);
189 190

module_param(recur_count, int, 0644);
191
MODULE_PARM_DESC(recur_count, " Recursion level for the stack overflow test");
192
module_param(cpoint_name, charp, 0444);
193
MODULE_PARM_DESC(cpoint_name, " Crash Point, where kernel is to be crashed");
194
module_param(cpoint_type, charp, 0444);
195 196 197 198 199
MODULE_PARM_DESC(cpoint_type, " Crash Point Type, action to be taken on "\
				"hitting the crash point");
module_param(cpoint_count, int, 0644);
MODULE_PARM_DESC(cpoint_count, " Crash Point Count, number of times the "\
				"crash point is to be hit to trigger action");
200

A
Adrian Bunk 已提交
201
static unsigned int jp_do_irq(unsigned int irq)
202 203 204 205 206 207
{
	lkdtm_handler();
	jprobe_return();
	return 0;
}

A
Adrian Bunk 已提交
208 209
static irqreturn_t jp_handle_irq_event(unsigned int irq,
				       struct irqaction *action)
210 211 212 213 214 215
{
	lkdtm_handler();
	jprobe_return();
	return 0;
}

A
Adrian Bunk 已提交
216
static void jp_tasklet_action(struct softirq_action *a)
217 218 219 220 221
{
	lkdtm_handler();
	jprobe_return();
}

A
Adrian Bunk 已提交
222
static void jp_ll_rw_block(int rw, int nr, struct buffer_head *bhs[])
223 224 225 226 227 228 229
{
	lkdtm_handler();
	jprobe_return();
}

struct scan_control;

A
Adrian Bunk 已提交
230 231 232
static unsigned long jp_shrink_inactive_list(unsigned long max_scan,
					     struct zone *zone,
					     struct scan_control *sc)
233 234 235 236 237 238
{
	lkdtm_handler();
	jprobe_return();
	return 0;
}

A
Adrian Bunk 已提交
239 240
static int jp_hrtimer_start(struct hrtimer *timer, ktime_t tim,
			    const enum hrtimer_mode mode)
241 242 243 244 245 246
{
	lkdtm_handler();
	jprobe_return();
	return 0;
}

A
Adrian Bunk 已提交
247
static int jp_scsi_dispatch_cmd(struct scsi_cmnd *cmd)
248 249 250 251 252 253 254
{
	lkdtm_handler();
	jprobe_return();
	return 0;
}

#ifdef CONFIG_IDE
255
static int jp_generic_ide_ioctl(ide_drive_t *drive, struct file *file,
256 257 258 259 260 261 262 263 264
			struct block_device *bdev, unsigned int cmd,
			unsigned long arg)
{
	lkdtm_handler();
	jprobe_return();
	return 0;
}
#endif

265 266 267 268 269 270 271 272 273 274
/* Return the crashpoint number or NONE if the name is invalid */
static enum ctype parse_cp_type(const char *what, size_t count)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(cp_type); i++) {
		if (!strcmp(what, cp_type[i]))
			return i + 1;
	}

N
Namhyung Kim 已提交
275
	return CT_NONE;
276 277 278 279
}

static const char *cp_type_to_str(enum ctype type)
{
N
Namhyung Kim 已提交
280
	if (type == CT_NONE || type < 0 || type > ARRAY_SIZE(cp_type))
281 282 283 284 285 286 287
		return "None";

	return cp_type[type - 1];
}

static const char *cp_name_to_str(enum cname name)
{
N
Namhyung Kim 已提交
288
	if (name == CN_INVALID || name < 0 || name > ARRAY_SIZE(cp_name))
289 290 291 292 293 294
		return "INVALID";

	return cp_name[name - 1];
}


295 296 297
static int lkdtm_parse_commandline(void)
{
	int i;
298
	unsigned long flags;
299

300
	if (cpoint_count < 1 || recur_count < 1)
301 302
		return -EINVAL;

303
	spin_lock_irqsave(&count_lock, flags);
304
	count = cpoint_count;
305
	spin_unlock_irqrestore(&count_lock, flags);
306 307 308 309 310 311 312 313 314 315

	/* 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;

	cptype = parse_cp_type(cpoint_type, strlen(cpoint_type));
N
Namhyung Kim 已提交
316
	if (cptype == CT_NONE)
317 318 319
		return -EINVAL;

	for (i = 0; i < ARRAY_SIZE(cp_name); i++) {
320 321
		if (!strcmp(cpoint_name, cp_name[i])) {
			cpoint = i + 1;
322
			return 0;
323 324 325
		}
	}

326 327
	/* Could not find a valid crash point */
	return -EINVAL;
328 329
}

330
static int recursive_loop(int remaining)
331
{
332
	char buf[REC_STACK_SIZE];
333

334 335 336
	/* Make sure compiler does not optimize this away. */
	memset(buf, (remaining & 0xff) | 0x1, REC_STACK_SIZE);
	if (!remaining)
337 338
		return 0;
	else
339
		return recursive_loop(remaining - 1);
340 341
}

K
Kees Cook 已提交
342 343 344 345 346 347 348 349
static noinline void corrupt_stack(void)
{
	/* Use default char array length that triggers stack protection. */
	char data[8];

	memset((void *)data, 0, 64);
}

350
static void lkdtm_do_action(enum ctype which)
351
{
352
	switch (which) {
N
Namhyung Kim 已提交
353
	case CT_PANIC:
354 355
		panic("dumptest");
		break;
N
Namhyung Kim 已提交
356
	case CT_BUG:
357 358
		BUG();
		break;
K
Kees Cook 已提交
359 360 361
	case CT_WARNING:
		WARN_ON(1);
		break;
N
Namhyung Kim 已提交
362
	case CT_EXCEPTION:
363 364
		*((int *) 0) = 0;
		break;
N
Namhyung Kim 已提交
365
	case CT_LOOP:
366 367 368
		for (;;)
			;
		break;
N
Namhyung Kim 已提交
369
	case CT_OVERFLOW:
370
		(void) recursive_loop(recur_count);
371
		break;
K
Kees Cook 已提交
372 373
	case CT_CORRUPT_STACK:
		corrupt_stack();
374
		break;
N
Namhyung Kim 已提交
375
	case CT_UNALIGNED_LOAD_STORE_WRITE: {
376 377 378 379 380 381 382 383 384 385 386
		static u8 data[5] __attribute__((aligned(4))) = {1, 2,
				3, 4, 5};
		u32 *p;
		u32 val = 0x12345678;

		p = (u32 *)(data + 1);
		if (*p == 0)
			val = 0x87654321;
		*p = val;
		 break;
	}
387 388
	case CT_OVERWRITE_ALLOCATION:
		lkdtm_OVERWRITE_ALLOCATION();
389
		break;
390 391
	case CT_WRITE_AFTER_FREE:
		lkdtm_WRITE_AFTER_FREE();
392
		break;
393 394
	case CT_READ_AFTER_FREE:
		lkdtm_READ_AFTER_FREE();
L
Laura Abbott 已提交
395
		break;
396 397
	case CT_WRITE_BUDDY_AFTER_FREE:
		lkdtm_WRITE_BUDDY_AFTER_FREE();
398
		break;
399 400
	case CT_READ_BUDDY_AFTER_FREE:
		lkdtm_READ_BUDDY_AFTER_FREE();
401
		break;
N
Namhyung Kim 已提交
402
	case CT_SOFTLOCKUP:
403 404 405 406
		preempt_disable();
		for (;;)
			cpu_relax();
		break;
N
Namhyung Kim 已提交
407
	case CT_HARDLOCKUP:
408 409 410 411
		local_irq_disable();
		for (;;)
			cpu_relax();
		break;
K
Kees Cook 已提交
412 413 414
	case CT_SPINLOCKUP:
		/* Must be called twice to trigger. */
		spin_lock(&lock_me_up);
K
Kees Cook 已提交
415 416
		/* Let sparse know we intended to exit holding the lock. */
		__release(&lock_me_up);
K
Kees Cook 已提交
417
		break;
N
Namhyung Kim 已提交
418
	case CT_HUNG_TASK:
419 420 421
		set_current_state(TASK_UNINTERRUPTIBLE);
		schedule();
		break;
K
Kees Cook 已提交
422
	case CT_EXEC_DATA:
423
		lkdtm_EXEC_DATA();
K
Kees Cook 已提交
424
		break;
425 426
	case CT_EXEC_STACK:
		lkdtm_EXEC_STACK();
K
Kees Cook 已提交
427
		break;
428 429
	case CT_EXEC_KMALLOC:
		lkdtm_EXEC_KMALLOC();
K
Kees Cook 已提交
430
		break;
431 432
	case CT_EXEC_VMALLOC:
		lkdtm_EXEC_VMALLOC();
K
Kees Cook 已提交
433
		break;
434
	case CT_EXEC_RODATA:
435
		lkdtm_EXEC_RODATA();
436
		break;
437 438
	case CT_EXEC_USERSPACE:
		lkdtm_EXEC_USERSPACE();
439
		break;
440 441
	case CT_ACCESS_USERSPACE:
		lkdtm_ACCESS_USERSPACE();
442
		break;
443 444
	case CT_WRITE_RO:
		lkdtm_WRITE_RO();
445
		break;
446 447
	case CT_WRITE_RO_AFTER_INIT:
		lkdtm_WRITE_RO_AFTER_INIT();
448
		break;
449 450
	case CT_WRITE_KERN:
		lkdtm_WRITE_KERN();
K
Kees Cook 已提交
451
		break;
452
	case CT_ATOMIC_UNDERFLOW: {
453 454
		atomic_t under = ATOMIC_INIT(INT_MIN);

455 456 457 458 459
		pr_info("attempting good atomic increment\n");
		atomic_inc(&under);
		atomic_dec(&under);

		pr_info("attempting bad atomic underflow\n");
460
		atomic_dec(&under);
461 462 463 464 465 466 467 468 469 470
		break;
	}
	case CT_ATOMIC_OVERFLOW: {
		atomic_t over = ATOMIC_INIT(INT_MAX);

		pr_info("attempting good atomic decrement\n");
		atomic_dec(&over);
		atomic_inc(&over);

		pr_info("attempting bad atomic overflow\n");
471 472 473 474
		atomic_inc(&over);

		return;
	}
K
Kees Cook 已提交
475
	case CT_USERCOPY_HEAP_SIZE_TO:
476
		lkdtm_USERCOPY_HEAP_SIZE_TO();
K
Kees Cook 已提交
477 478
		break;
	case CT_USERCOPY_HEAP_SIZE_FROM:
479
		lkdtm_USERCOPY_HEAP_SIZE_FROM();
K
Kees Cook 已提交
480 481
		break;
	case CT_USERCOPY_HEAP_FLAG_TO:
482
		lkdtm_USERCOPY_HEAP_FLAG_TO();
K
Kees Cook 已提交
483 484
		break;
	case CT_USERCOPY_HEAP_FLAG_FROM:
485
		lkdtm_USERCOPY_HEAP_FLAG_FROM();
K
Kees Cook 已提交
486 487
		break;
	case CT_USERCOPY_STACK_FRAME_TO:
488
		lkdtm_USERCOPY_STACK_FRAME_TO();
K
Kees Cook 已提交
489 490
		break;
	case CT_USERCOPY_STACK_FRAME_FROM:
491
		lkdtm_USERCOPY_STACK_FRAME_FROM();
K
Kees Cook 已提交
492 493
		break;
	case CT_USERCOPY_STACK_BEYOND:
494
		lkdtm_USERCOPY_STACK_BEYOND();
K
Kees Cook 已提交
495
		break;
496
	case CT_USERCOPY_KERNEL:
497
		lkdtm_USERCOPY_KERNEL();
498
		break;
N
Namhyung Kim 已提交
499
	case CT_NONE:
500 501 502 503 504 505 506 507
	default:
		break;
	}

}

static void lkdtm_handler(void)
{
508
	unsigned long flags;
509
	bool do_it = false;
510 511

	spin_lock_irqsave(&count_lock, flags);
512
	count--;
513 514
	pr_info("Crash point %s of type %s hit, trigger in %d rounds\n",
		cp_name_to_str(cpoint), cp_type_to_str(cptype), count);
515 516

	if (count == 0) {
517
		do_it = true;
518 519
		count = cpoint_count;
	}
520
	spin_unlock_irqrestore(&count_lock, flags);
521 522 523

	if (do_it)
		lkdtm_do_action(cptype);
524 525
}

526
static int lkdtm_register_cpoint(enum cname which)
527 528 529
{
	int ret;

N
Namhyung Kim 已提交
530
	cpoint = CN_INVALID;
531 532
	if (lkdtm.entry != NULL)
		unregister_jprobe(&lkdtm);
533

534
	switch (which) {
N
Namhyung Kim 已提交
535
	case CN_DIRECT:
536 537
		lkdtm_do_action(cptype);
		return 0;
N
Namhyung Kim 已提交
538
	case CN_INT_HARDWARE_ENTRY:
M
M. Mohan Kumar 已提交
539
		lkdtm.kp.symbol_name = "do_IRQ";
540 541
		lkdtm.entry = (kprobe_opcode_t*) jp_do_irq;
		break;
N
Namhyung Kim 已提交
542
	case CN_INT_HW_IRQ_EN:
543 544 545
		lkdtm.kp.symbol_name = "handle_IRQ_event";
		lkdtm.entry = (kprobe_opcode_t*) jp_handle_irq_event;
		break;
N
Namhyung Kim 已提交
546
	case CN_INT_TASKLET_ENTRY:
547 548 549
		lkdtm.kp.symbol_name = "tasklet_action";
		lkdtm.entry = (kprobe_opcode_t*) jp_tasklet_action;
		break;
N
Namhyung Kim 已提交
550
	case CN_FS_DEVRW:
551 552 553
		lkdtm.kp.symbol_name = "ll_rw_block";
		lkdtm.entry = (kprobe_opcode_t*) jp_ll_rw_block;
		break;
N
Namhyung Kim 已提交
554
	case CN_MEM_SWAPOUT:
555 556
		lkdtm.kp.symbol_name = "shrink_inactive_list";
		lkdtm.entry = (kprobe_opcode_t*) jp_shrink_inactive_list;
557
		break;
N
Namhyung Kim 已提交
558
	case CN_TIMERADD:
559 560 561
		lkdtm.kp.symbol_name = "hrtimer_start";
		lkdtm.entry = (kprobe_opcode_t*) jp_hrtimer_start;
		break;
N
Namhyung Kim 已提交
562
	case CN_SCSI_DISPATCH_CMD:
563 564 565
		lkdtm.kp.symbol_name = "scsi_dispatch_cmd";
		lkdtm.entry = (kprobe_opcode_t*) jp_scsi_dispatch_cmd;
		break;
N
Namhyung Kim 已提交
566
	case CN_IDE_CORE_CP:
567 568 569 570
#ifdef CONFIG_IDE
		lkdtm.kp.symbol_name = "generic_ide_ioctl";
		lkdtm.entry = (kprobe_opcode_t*) jp_generic_ide_ioctl;
#else
571
		pr_info("Crash point not available\n");
572
		return -EINVAL;
573 574 575
#endif
		break;
	default:
576
		pr_info("Invalid Crash Point\n");
577
		return -EINVAL;
578 579
	}

580
	cpoint = which;
581
	if ((ret = register_jprobe(&lkdtm)) < 0) {
582
		pr_info("Couldn't register jprobe\n");
N
Namhyung Kim 已提交
583
		cpoint = CN_INVALID;
584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
	}

	return ret;
}

static ssize_t do_register_entry(enum cname which, struct file *f,
		const char __user *user_buf, size_t count, loff_t *off)
{
	char *buf;
	int err;

	if (count >= PAGE_SIZE)
		return -EINVAL;

	buf = (char *)__get_free_page(GFP_KERNEL);
	if (!buf)
		return -ENOMEM;
	if (copy_from_user(buf, user_buf, count)) {
		free_page((unsigned long) buf);
		return -EFAULT;
	}
	/* NULL-terminate and remove enter */
	buf[count] = '\0';
	strim(buf);

	cptype = parse_cp_type(buf, count);
	free_page((unsigned long) buf);

N
Namhyung Kim 已提交
612
	if (cptype == CT_NONE)
613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
		return -EINVAL;

	err = lkdtm_register_cpoint(which);
	if (err < 0)
		return err;

	*off += count;

	return count;
}

/* Generic read callback that just prints out the available crash types */
static ssize_t lkdtm_debugfs_read(struct file *f, char __user *user_buf,
		size_t count, loff_t *off)
{
	char *buf;
	int i, n, out;

	buf = (char *)__get_free_page(GFP_KERNEL);
632 633
	if (buf == NULL)
		return -ENOMEM;
634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655

	n = snprintf(buf, PAGE_SIZE, "Available crash types:\n");
	for (i = 0; i < ARRAY_SIZE(cp_type); i++)
		n += snprintf(buf + n, PAGE_SIZE - n, "%s\n", cp_type[i]);
	buf[n] = '\0';

	out = simple_read_from_buffer(user_buf, count, off,
				      buf, n);
	free_page((unsigned long) buf);

	return out;
}

static int lkdtm_debugfs_open(struct inode *inode, struct file *file)
{
	return 0;
}


static ssize_t int_hardware_entry(struct file *f, const char __user *buf,
		size_t count, loff_t *off)
{
N
Namhyung Kim 已提交
656
	return do_register_entry(CN_INT_HARDWARE_ENTRY, f, buf, count, off);
657 658 659 660 661
}

static ssize_t int_hw_irq_en(struct file *f, const char __user *buf,
		size_t count, loff_t *off)
{
N
Namhyung Kim 已提交
662
	return do_register_entry(CN_INT_HW_IRQ_EN, f, buf, count, off);
663 664 665 666 667
}

static ssize_t int_tasklet_entry(struct file *f, const char __user *buf,
		size_t count, loff_t *off)
{
N
Namhyung Kim 已提交
668
	return do_register_entry(CN_INT_TASKLET_ENTRY, f, buf, count, off);
669 670 671 672 673
}

static ssize_t fs_devrw_entry(struct file *f, const char __user *buf,
		size_t count, loff_t *off)
{
N
Namhyung Kim 已提交
674
	return do_register_entry(CN_FS_DEVRW, f, buf, count, off);
675 676 677 678 679
}

static ssize_t mem_swapout_entry(struct file *f, const char __user *buf,
		size_t count, loff_t *off)
{
N
Namhyung Kim 已提交
680
	return do_register_entry(CN_MEM_SWAPOUT, f, buf, count, off);
681 682 683 684 685
}

static ssize_t timeradd_entry(struct file *f, const char __user *buf,
		size_t count, loff_t *off)
{
N
Namhyung Kim 已提交
686
	return do_register_entry(CN_TIMERADD, f, buf, count, off);
687 688 689 690 691
}

static ssize_t scsi_dispatch_cmd_entry(struct file *f,
		const char __user *buf, size_t count, loff_t *off)
{
N
Namhyung Kim 已提交
692
	return do_register_entry(CN_SCSI_DISPATCH_CMD, f, buf, count, off);
693 694 695 696 697
}

static ssize_t ide_core_cp_entry(struct file *f, const char __user *buf,
		size_t count, loff_t *off)
{
N
Namhyung Kim 已提交
698
	return do_register_entry(CN_IDE_CORE_CP, f, buf, count, off);
699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725
}

/* Special entry to just crash directly. Available without KPROBEs */
static ssize_t direct_entry(struct file *f, const char __user *user_buf,
		size_t count, loff_t *off)
{
	enum ctype type;
	char *buf;

	if (count >= PAGE_SIZE)
		return -EINVAL;
	if (count < 1)
		return -EINVAL;

	buf = (char *)__get_free_page(GFP_KERNEL);
	if (!buf)
		return -ENOMEM;
	if (copy_from_user(buf, user_buf, count)) {
		free_page((unsigned long) buf);
		return -EFAULT;
	}
	/* NULL-terminate and remove enter */
	buf[count] = '\0';
	strim(buf);

	type = parse_cp_type(buf, count);
	free_page((unsigned long) buf);
N
Namhyung Kim 已提交
726
	if (type == CT_NONE)
727 728
		return -EINVAL;

729
	pr_info("Performing direct entry %s\n", cp_type_to_str(type));
730 731 732 733 734 735 736 737 738 739 740 741 742
	lkdtm_do_action(type);
	*off += 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,
743
			.llseek = generic_file_llseek,
744 745 746
			.open = lkdtm_debugfs_open,
			.write = direct_entry} },
	{"INT_HARDWARE_ENTRY", {.read = lkdtm_debugfs_read,
747
			.llseek = generic_file_llseek,
748 749 750
			.open = lkdtm_debugfs_open,
			.write = int_hardware_entry} },
	{"INT_HW_IRQ_EN", {.read = lkdtm_debugfs_read,
751
			.llseek = generic_file_llseek,
752 753 754
			.open = lkdtm_debugfs_open,
			.write = int_hw_irq_en} },
	{"INT_TASKLET_ENTRY", {.read = lkdtm_debugfs_read,
755
			.llseek = generic_file_llseek,
756 757 758
			.open = lkdtm_debugfs_open,
			.write = int_tasklet_entry} },
	{"FS_DEVRW", {.read = lkdtm_debugfs_read,
759
			.llseek = generic_file_llseek,
760 761 762
			.open = lkdtm_debugfs_open,
			.write = fs_devrw_entry} },
	{"MEM_SWAPOUT", {.read = lkdtm_debugfs_read,
763
			.llseek = generic_file_llseek,
764 765 766
			.open = lkdtm_debugfs_open,
			.write = mem_swapout_entry} },
	{"TIMERADD", {.read = lkdtm_debugfs_read,
767
			.llseek = generic_file_llseek,
768 769 770
			.open = lkdtm_debugfs_open,
			.write = timeradd_entry} },
	{"SCSI_DISPATCH_CMD", {.read = lkdtm_debugfs_read,
771
			.llseek = generic_file_llseek,
772 773 774
			.open = lkdtm_debugfs_open,
			.write = scsi_dispatch_cmd_entry} },
	{"IDE_CORE_CP",	{.read = lkdtm_debugfs_read,
775
			.llseek = generic_file_llseek,
776 777 778 779 780 781 782 783 784 785 786 787
			.open = lkdtm_debugfs_open,
			.write = ide_core_cp_entry} },
};

static struct dentry *lkdtm_debugfs_root;

static int __init lkdtm_module_init(void)
{
	int ret = -EINVAL;
	int n_debugfs_entries = 1; /* Assume only the direct entry */
	int i;

788
	/* Handle test-specific initialization. */
789
	lkdtm_perms_init();
790 791
	lkdtm_usercopy_init();

792 793 794
	/* Register debugfs interface */
	lkdtm_debugfs_root = debugfs_create_dir("provoke-crash", NULL);
	if (!lkdtm_debugfs_root) {
795
		pr_err("creating root dir failed\n");
796 797 798 799 800 801 802 803 804 805 806 807 808 809
		return -ENODEV;
	}

#ifdef CONFIG_KPROBES
	n_debugfs_entries = ARRAY_SIZE(crash_entries);
#endif

	for (i = 0; i < n_debugfs_entries; i++) {
		const struct crash_entry *cur = &crash_entries[i];
		struct dentry *de;

		de = debugfs_create_file(cur->name, 0644, lkdtm_debugfs_root,
				NULL, &cur->fops);
		if (de == NULL) {
810
			pr_err("could not create %s\n", cur->name);
811 812 813 814 815
			goto out_err;
		}
	}

	if (lkdtm_parse_commandline() == -EINVAL) {
816
		pr_info("Invalid command\n");
817 818 819
		goto out_err;
	}

N
Namhyung Kim 已提交
820
	if (cpoint != CN_INVALID && cptype != CT_NONE) {
821 822
		ret = lkdtm_register_cpoint(cpoint);
		if (ret < 0) {
823
			pr_info("Invalid crash point %d\n", cpoint);
824 825
			goto out_err;
		}
826 827
		pr_info("Crash point %s of type %s registered\n",
			cpoint_name, cpoint_type);
828
	} else {
829
		pr_info("No crash points registered, enable through debugfs\n");
830 831 832
	}

	return 0;
833 834 835 836

out_err:
	debugfs_remove_recursive(lkdtm_debugfs_root);
	return ret;
837 838
}

A
Adrian Bunk 已提交
839
static void __exit lkdtm_module_exit(void)
840
{
841 842
	debugfs_remove_recursive(lkdtm_debugfs_root);

843 844
	/* Handle test-specific clean-up. */
	lkdtm_usercopy_exit();
K
Kees Cook 已提交
845

846
	unregister_jprobe(&lkdtm);
847
	pr_info("Crash point unregistered\n");
848 849 850 851 852 853
}

module_init(lkdtm_module_init);
module_exit(lkdtm_module_exit);

MODULE_LICENSE("GPL");
854
MODULE_DESCRIPTION("Kprobe module for testing crash dumps");