lkdtm_core.c 21.6 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;
	}
N
Namhyung Kim 已提交
387
	case CT_OVERWRITE_ALLOCATION: {
388 389 390 391 392 393 394
		size_t len = 1020;
		u32 *data = kmalloc(len, GFP_KERNEL);

		data[1024 / sizeof(u32)] = 0x12345678;
		kfree(data);
		break;
	}
N
Namhyung Kim 已提交
395
	case CT_WRITE_AFTER_FREE: {
K
Kees Cook 已提交
396
		int *base, *again;
397
		size_t len = 1024;
398 399 400 401 402 403
		/*
		 * The slub allocator uses the first word to store the free
		 * pointer in some configurations. Use the middle of the
		 * allocation to avoid running into the freelist
		 */
		size_t offset = (len / sizeof(*base)) / 2;
404

405 406 407 408
		base = kmalloc(len, GFP_KERNEL);
		pr_info("Allocated memory %p-%p\n", base, &base[offset * 2]);
		pr_info("Attempting bad write to freed memory at %p\n",
			&base[offset]);
K
Kees Cook 已提交
409
		kfree(base);
410
		base[offset] = 0x0abcdef0;
K
Kees Cook 已提交
411 412 413 414 415
		/* Attempt to notice the overwrite. */
		again = kmalloc(len, GFP_KERNEL);
		kfree(again);
		if (again != base)
			pr_info("Hmm, didn't get the same memory range.\n");
416 417 418

		break;
	}
L
Laura Abbott 已提交
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
	case CT_READ_AFTER_FREE: {
		int *base, *val, saw;
		size_t len = 1024;
		/*
		 * The slub allocator uses the first word to store the free
		 * pointer in some configurations. Use the middle of the
		 * allocation to avoid running into the freelist
		 */
		size_t offset = (len / sizeof(*base)) / 2;

		base = kmalloc(len, GFP_KERNEL);
		if (!base)
			break;

		val = kmalloc(len, GFP_KERNEL);
S
Sudip Mukherjee 已提交
434 435
		if (!val) {
			kfree(base);
L
Laura Abbott 已提交
436
			break;
S
Sudip Mukherjee 已提交
437
		}
L
Laura Abbott 已提交
438 439 440 441 442 443 444 445 446 447 448

		*val = 0x12345678;
		base[offset] = *val;
		pr_info("Value in memory before free: %x\n", base[offset]);

		kfree(base);

		pr_info("Attempting bad read from freed memory\n");
		saw = base[offset];
		if (saw != *val) {
			/* Good! Poisoning happened, so declare a win. */
K
Kees Cook 已提交
449
			pr_info("Memory correctly poisoned (%x)\n", saw);
L
Laura Abbott 已提交
450 451 452 453 454 455 456
			BUG();
		}
		pr_info("Memory was not poisoned\n");

		kfree(val);
		break;
	}
457 458 459 460 461 462 463
	case CT_WRITE_BUDDY_AFTER_FREE: {
		unsigned long p = __get_free_page(GFP_KERNEL);
		if (!p)
			break;
		pr_info("Writing to the buddy page before free\n");
		memset((void *)p, 0x3, PAGE_SIZE);
		free_page(p);
464
		schedule();
465 466
		pr_info("Attempting bad write to the buddy page after free\n");
		memset((void *)p, 0x78, PAGE_SIZE);
K
Kees Cook 已提交
467 468 469
		/* Attempt to notice the overwrite. */
		p = __get_free_page(GFP_KERNEL);
		free_page(p);
470
		schedule();
K
Kees Cook 已提交
471

472 473 474 475
		break;
	}
	case CT_READ_BUDDY_AFTER_FREE: {
		unsigned long p = __get_free_page(GFP_KERNEL);
S
Sudip Mukherjee 已提交
476
		int saw, *val;
477 478 479 480 481
		int *base;

		if (!p)
			break;

S
Sudip Mukherjee 已提交
482
		val = kmalloc(1024, GFP_KERNEL);
483 484
		if (!val) {
			free_page(p);
485
			break;
486
		}
487 488 489 490 491 492 493 494 495 496 497

		base = (int *)p;

		*val = 0x12345678;
		base[0] = *val;
		pr_info("Value in memory before free: %x\n", base[0]);
		free_page(p);
		pr_info("Attempting to read from freed memory\n");
		saw = base[0];
		if (saw != *val) {
			/* Good! Poisoning happened, so declare a win. */
K
Kees Cook 已提交
498
			pr_info("Memory correctly poisoned (%x)\n", saw);
499 500 501 502 503
			BUG();
		}
		pr_info("Buddy page was not poisoned\n");

		kfree(val);
504 505
		break;
	}
N
Namhyung Kim 已提交
506
	case CT_SOFTLOCKUP:
507 508 509 510
		preempt_disable();
		for (;;)
			cpu_relax();
		break;
N
Namhyung Kim 已提交
511
	case CT_HARDLOCKUP:
512 513 514 515
		local_irq_disable();
		for (;;)
			cpu_relax();
		break;
K
Kees Cook 已提交
516 517 518
	case CT_SPINLOCKUP:
		/* Must be called twice to trigger. */
		spin_lock(&lock_me_up);
K
Kees Cook 已提交
519 520
		/* Let sparse know we intended to exit holding the lock. */
		__release(&lock_me_up);
K
Kees Cook 已提交
521
		break;
N
Namhyung Kim 已提交
522
	case CT_HUNG_TASK:
523 524 525
		set_current_state(TASK_UNINTERRUPTIBLE);
		schedule();
		break;
K
Kees Cook 已提交
526
	case CT_EXEC_DATA:
527
		lkdtm_EXEC_DATA();
K
Kees Cook 已提交
528
		break;
529 530
	case CT_EXEC_STACK:
		lkdtm_EXEC_STACK();
K
Kees Cook 已提交
531
		break;
532 533
	case CT_EXEC_KMALLOC:
		lkdtm_EXEC_KMALLOC();
K
Kees Cook 已提交
534
		break;
535 536
	case CT_EXEC_VMALLOC:
		lkdtm_EXEC_VMALLOC();
K
Kees Cook 已提交
537
		break;
538
	case CT_EXEC_RODATA:
539
		lkdtm_EXEC_RODATA();
540
		break;
541 542
	case CT_EXEC_USERSPACE:
		lkdtm_EXEC_USERSPACE();
543
		break;
544 545
	case CT_ACCESS_USERSPACE:
		lkdtm_ACCESS_USERSPACE();
546
		break;
547 548
	case CT_WRITE_RO:
		lkdtm_WRITE_RO();
549
		break;
550 551
	case CT_WRITE_RO_AFTER_INIT:
		lkdtm_WRITE_RO_AFTER_INIT();
552
		break;
553 554
	case CT_WRITE_KERN:
		lkdtm_WRITE_KERN();
K
Kees Cook 已提交
555
		break;
556
	case CT_ATOMIC_UNDERFLOW: {
557 558
		atomic_t under = ATOMIC_INIT(INT_MIN);

559 560 561 562 563
		pr_info("attempting good atomic increment\n");
		atomic_inc(&under);
		atomic_dec(&under);

		pr_info("attempting bad atomic underflow\n");
564
		atomic_dec(&under);
565 566 567 568 569 570 571 572 573 574
		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");
575 576 577 578
		atomic_inc(&over);

		return;
	}
K
Kees Cook 已提交
579
	case CT_USERCOPY_HEAP_SIZE_TO:
580
		lkdtm_USERCOPY_HEAP_SIZE_TO();
K
Kees Cook 已提交
581 582
		break;
	case CT_USERCOPY_HEAP_SIZE_FROM:
583
		lkdtm_USERCOPY_HEAP_SIZE_FROM();
K
Kees Cook 已提交
584 585
		break;
	case CT_USERCOPY_HEAP_FLAG_TO:
586
		lkdtm_USERCOPY_HEAP_FLAG_TO();
K
Kees Cook 已提交
587 588
		break;
	case CT_USERCOPY_HEAP_FLAG_FROM:
589
		lkdtm_USERCOPY_HEAP_FLAG_FROM();
K
Kees Cook 已提交
590 591
		break;
	case CT_USERCOPY_STACK_FRAME_TO:
592
		lkdtm_USERCOPY_STACK_FRAME_TO();
K
Kees Cook 已提交
593 594
		break;
	case CT_USERCOPY_STACK_FRAME_FROM:
595
		lkdtm_USERCOPY_STACK_FRAME_FROM();
K
Kees Cook 已提交
596 597
		break;
	case CT_USERCOPY_STACK_BEYOND:
598
		lkdtm_USERCOPY_STACK_BEYOND();
K
Kees Cook 已提交
599
		break;
600
	case CT_USERCOPY_KERNEL:
601
		lkdtm_USERCOPY_KERNEL();
602
		break;
N
Namhyung Kim 已提交
603
	case CT_NONE:
604 605 606 607 608 609 610 611
	default:
		break;
	}

}

static void lkdtm_handler(void)
{
612
	unsigned long flags;
613
	bool do_it = false;
614 615

	spin_lock_irqsave(&count_lock, flags);
616
	count--;
617 618
	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);
619 620

	if (count == 0) {
621
		do_it = true;
622 623
		count = cpoint_count;
	}
624
	spin_unlock_irqrestore(&count_lock, flags);
625 626 627

	if (do_it)
		lkdtm_do_action(cptype);
628 629
}

630
static int lkdtm_register_cpoint(enum cname which)
631 632 633
{
	int ret;

N
Namhyung Kim 已提交
634
	cpoint = CN_INVALID;
635 636
	if (lkdtm.entry != NULL)
		unregister_jprobe(&lkdtm);
637

638
	switch (which) {
N
Namhyung Kim 已提交
639
	case CN_DIRECT:
640 641
		lkdtm_do_action(cptype);
		return 0;
N
Namhyung Kim 已提交
642
	case CN_INT_HARDWARE_ENTRY:
M
M. Mohan Kumar 已提交
643
		lkdtm.kp.symbol_name = "do_IRQ";
644 645
		lkdtm.entry = (kprobe_opcode_t*) jp_do_irq;
		break;
N
Namhyung Kim 已提交
646
	case CN_INT_HW_IRQ_EN:
647 648 649
		lkdtm.kp.symbol_name = "handle_IRQ_event";
		lkdtm.entry = (kprobe_opcode_t*) jp_handle_irq_event;
		break;
N
Namhyung Kim 已提交
650
	case CN_INT_TASKLET_ENTRY:
651 652 653
		lkdtm.kp.symbol_name = "tasklet_action";
		lkdtm.entry = (kprobe_opcode_t*) jp_tasklet_action;
		break;
N
Namhyung Kim 已提交
654
	case CN_FS_DEVRW:
655 656 657
		lkdtm.kp.symbol_name = "ll_rw_block";
		lkdtm.entry = (kprobe_opcode_t*) jp_ll_rw_block;
		break;
N
Namhyung Kim 已提交
658
	case CN_MEM_SWAPOUT:
659 660
		lkdtm.kp.symbol_name = "shrink_inactive_list";
		lkdtm.entry = (kprobe_opcode_t*) jp_shrink_inactive_list;
661
		break;
N
Namhyung Kim 已提交
662
	case CN_TIMERADD:
663 664 665
		lkdtm.kp.symbol_name = "hrtimer_start";
		lkdtm.entry = (kprobe_opcode_t*) jp_hrtimer_start;
		break;
N
Namhyung Kim 已提交
666
	case CN_SCSI_DISPATCH_CMD:
667 668 669
		lkdtm.kp.symbol_name = "scsi_dispatch_cmd";
		lkdtm.entry = (kprobe_opcode_t*) jp_scsi_dispatch_cmd;
		break;
N
Namhyung Kim 已提交
670
	case CN_IDE_CORE_CP:
671 672 673 674
#ifdef CONFIG_IDE
		lkdtm.kp.symbol_name = "generic_ide_ioctl";
		lkdtm.entry = (kprobe_opcode_t*) jp_generic_ide_ioctl;
#else
675
		pr_info("Crash point not available\n");
676
		return -EINVAL;
677 678 679
#endif
		break;
	default:
680
		pr_info("Invalid Crash Point\n");
681
		return -EINVAL;
682 683
	}

684
	cpoint = which;
685
	if ((ret = register_jprobe(&lkdtm)) < 0) {
686
		pr_info("Couldn't register jprobe\n");
N
Namhyung Kim 已提交
687
		cpoint = CN_INVALID;
688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715
	}

	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 已提交
716
	if (cptype == CT_NONE)
717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735
		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);
736 737
	if (buf == NULL)
		return -ENOMEM;
738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759

	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 已提交
760
	return do_register_entry(CN_INT_HARDWARE_ENTRY, f, buf, count, off);
761 762 763 764 765
}

static ssize_t int_hw_irq_en(struct file *f, const char __user *buf,
		size_t count, loff_t *off)
{
N
Namhyung Kim 已提交
766
	return do_register_entry(CN_INT_HW_IRQ_EN, f, buf, count, off);
767 768 769 770 771
}

static ssize_t int_tasklet_entry(struct file *f, const char __user *buf,
		size_t count, loff_t *off)
{
N
Namhyung Kim 已提交
772
	return do_register_entry(CN_INT_TASKLET_ENTRY, f, buf, count, off);
773 774 775 776 777
}

static ssize_t fs_devrw_entry(struct file *f, const char __user *buf,
		size_t count, loff_t *off)
{
N
Namhyung Kim 已提交
778
	return do_register_entry(CN_FS_DEVRW, f, buf, count, off);
779 780 781 782 783
}

static ssize_t mem_swapout_entry(struct file *f, const char __user *buf,
		size_t count, loff_t *off)
{
N
Namhyung Kim 已提交
784
	return do_register_entry(CN_MEM_SWAPOUT, f, buf, count, off);
785 786 787 788 789
}

static ssize_t timeradd_entry(struct file *f, const char __user *buf,
		size_t count, loff_t *off)
{
N
Namhyung Kim 已提交
790
	return do_register_entry(CN_TIMERADD, f, buf, count, off);
791 792 793 794 795
}

static ssize_t scsi_dispatch_cmd_entry(struct file *f,
		const char __user *buf, size_t count, loff_t *off)
{
N
Namhyung Kim 已提交
796
	return do_register_entry(CN_SCSI_DISPATCH_CMD, f, buf, count, off);
797 798 799 800 801
}

static ssize_t ide_core_cp_entry(struct file *f, const char __user *buf,
		size_t count, loff_t *off)
{
N
Namhyung Kim 已提交
802
	return do_register_entry(CN_IDE_CORE_CP, f, buf, count, off);
803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829
}

/* 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 已提交
830
	if (type == CT_NONE)
831 832
		return -EINVAL;

833
	pr_info("Performing direct entry %s\n", cp_type_to_str(type));
834 835 836 837 838 839 840 841 842 843 844 845 846
	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,
847
			.llseek = generic_file_llseek,
848 849 850
			.open = lkdtm_debugfs_open,
			.write = direct_entry} },
	{"INT_HARDWARE_ENTRY", {.read = lkdtm_debugfs_read,
851
			.llseek = generic_file_llseek,
852 853 854
			.open = lkdtm_debugfs_open,
			.write = int_hardware_entry} },
	{"INT_HW_IRQ_EN", {.read = lkdtm_debugfs_read,
855
			.llseek = generic_file_llseek,
856 857 858
			.open = lkdtm_debugfs_open,
			.write = int_hw_irq_en} },
	{"INT_TASKLET_ENTRY", {.read = lkdtm_debugfs_read,
859
			.llseek = generic_file_llseek,
860 861 862
			.open = lkdtm_debugfs_open,
			.write = int_tasklet_entry} },
	{"FS_DEVRW", {.read = lkdtm_debugfs_read,
863
			.llseek = generic_file_llseek,
864 865 866
			.open = lkdtm_debugfs_open,
			.write = fs_devrw_entry} },
	{"MEM_SWAPOUT", {.read = lkdtm_debugfs_read,
867
			.llseek = generic_file_llseek,
868 869 870
			.open = lkdtm_debugfs_open,
			.write = mem_swapout_entry} },
	{"TIMERADD", {.read = lkdtm_debugfs_read,
871
			.llseek = generic_file_llseek,
872 873 874
			.open = lkdtm_debugfs_open,
			.write = timeradd_entry} },
	{"SCSI_DISPATCH_CMD", {.read = lkdtm_debugfs_read,
875
			.llseek = generic_file_llseek,
876 877 878
			.open = lkdtm_debugfs_open,
			.write = scsi_dispatch_cmd_entry} },
	{"IDE_CORE_CP",	{.read = lkdtm_debugfs_read,
879
			.llseek = generic_file_llseek,
880 881 882 883 884 885 886 887 888 889 890 891
			.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;

892
	/* Handle test-specific initialization. */
893
	lkdtm_perms_init();
894 895
	lkdtm_usercopy_init();

896 897 898
	/* Register debugfs interface */
	lkdtm_debugfs_root = debugfs_create_dir("provoke-crash", NULL);
	if (!lkdtm_debugfs_root) {
899
		pr_err("creating root dir failed\n");
900 901 902 903 904 905 906 907 908 909 910 911 912 913
		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) {
914
			pr_err("could not create %s\n", cur->name);
915 916 917 918 919
			goto out_err;
		}
	}

	if (lkdtm_parse_commandline() == -EINVAL) {
920
		pr_info("Invalid command\n");
921 922 923
		goto out_err;
	}

N
Namhyung Kim 已提交
924
	if (cpoint != CN_INVALID && cptype != CT_NONE) {
925 926
		ret = lkdtm_register_cpoint(cpoint);
		if (ret < 0) {
927
			pr_info("Invalid crash point %d\n", cpoint);
928 929
			goto out_err;
		}
930 931
		pr_info("Crash point %s of type %s registered\n",
			cpoint_name, cpoint_type);
932
	} else {
933
		pr_info("No crash points registered, enable through debugfs\n");
934 935 936
	}

	return 0;
937 938 939 940

out_err:
	debugfs_remove_recursive(lkdtm_debugfs_root);
	return ret;
941 942
}

A
Adrian Bunk 已提交
943
static void __exit lkdtm_module_exit(void)
944
{
945 946
	debugfs_remove_recursive(lkdtm_debugfs_root);

947 948
	/* Handle test-specific clean-up. */
	lkdtm_usercopy_exit();
K
Kees Cook 已提交
949

950
	unregister_jprobe(&lkdtm);
951
	pr_info("Crash point unregistered\n");
952 953 954 955 956 957
}

module_init(lkdtm_module_init);
module_exit(lkdtm_module_exit);

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