lkdtm_core.c 23.3 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>
K
Kees Cook 已提交
47
#include <linux/vmalloc.h>
48
#include <linux/mman.h>
K
Kees Cook 已提交
49
#include <asm/cacheflush.h>
50 51 52 53 54

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

55 56 57 58 59 60 61 62 63 64 65 66
/*
 * 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)

67
#define DEFAULT_COUNT 10
K
Kees Cook 已提交
68
#define EXEC_SIZE 64
69 70

enum cname {
N
Namhyung Kim 已提交
71 72 73 74 75 76 77 78 79 80
	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,
81 82 83
};

enum ctype {
N
Namhyung Kim 已提交
84 85 86
	CT_NONE,
	CT_PANIC,
	CT_BUG,
K
Kees Cook 已提交
87
	CT_WARNING,
N
Namhyung Kim 已提交
88 89 90 91 92 93 94
	CT_EXCEPTION,
	CT_LOOP,
	CT_OVERFLOW,
	CT_CORRUPT_STACK,
	CT_UNALIGNED_LOAD_STORE_WRITE,
	CT_OVERWRITE_ALLOCATION,
	CT_WRITE_AFTER_FREE,
L
Laura Abbott 已提交
95
	CT_READ_AFTER_FREE,
96 97
	CT_WRITE_BUDDY_AFTER_FREE,
	CT_READ_BUDDY_AFTER_FREE,
N
Namhyung Kim 已提交
98 99
	CT_SOFTLOCKUP,
	CT_HARDLOCKUP,
K
Kees Cook 已提交
100
	CT_SPINLOCKUP,
N
Namhyung Kim 已提交
101
	CT_HUNG_TASK,
K
Kees Cook 已提交
102 103 104 105
	CT_EXEC_DATA,
	CT_EXEC_STACK,
	CT_EXEC_KMALLOC,
	CT_EXEC_VMALLOC,
106 107 108
	CT_EXEC_USERSPACE,
	CT_ACCESS_USERSPACE,
	CT_WRITE_RO,
109
	CT_WRITE_RO_AFTER_INIT,
K
Kees Cook 已提交
110
	CT_WRITE_KERN,
111
	CT_WRAP_ATOMIC
112 113 114 115 116 117 118 119 120 121
};

static char* cp_name[] = {
	"INT_HARDWARE_ENTRY",
	"INT_HW_IRQ_EN",
	"INT_TASKLET_ENTRY",
	"FS_DEVRW",
	"MEM_SWAPOUT",
	"TIMERADD",
	"SCSI_DISPATCH_CMD",
122 123
	"IDE_CORE_CP",
	"DIRECT",
124 125 126 127 128
};

static char* cp_type[] = {
	"PANIC",
	"BUG",
K
Kees Cook 已提交
129
	"WARNING",
130 131
	"EXCEPTION",
	"LOOP",
132 133 134 135 136
	"OVERFLOW",
	"CORRUPT_STACK",
	"UNALIGNED_LOAD_STORE_WRITE",
	"OVERWRITE_ALLOCATION",
	"WRITE_AFTER_FREE",
L
Laura Abbott 已提交
137
	"READ_AFTER_FREE",
138 139
	"WRITE_BUDDY_AFTER_FREE",
	"READ_BUDDY_AFTER_FREE",
140 141
	"SOFTLOCKUP",
	"HARDLOCKUP",
K
Kees Cook 已提交
142
	"SPINLOCKUP",
143
	"HUNG_TASK",
K
Kees Cook 已提交
144 145 146 147
	"EXEC_DATA",
	"EXEC_STACK",
	"EXEC_KMALLOC",
	"EXEC_VMALLOC",
148 149 150
	"EXEC_USERSPACE",
	"ACCESS_USERSPACE",
	"WRITE_RO",
151
	"WRITE_RO_AFTER_INIT",
K
Kees Cook 已提交
152
	"WRITE_KERN",
153
	"WRAP_ATOMIC"
154 155 156 157 158 159 160
};

static struct jprobe lkdtm;

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

161 162
static char* cpoint_name;
static char* cpoint_type;
163 164 165
static int cpoint_count = DEFAULT_COUNT;
static int recur_count = REC_NUM_DEFAULT;

N
Namhyung Kim 已提交
166 167
static enum cname cpoint = CN_INVALID;
static enum ctype cptype = CT_NONE;
168
static int count = DEFAULT_COUNT;
169
static DEFINE_SPINLOCK(count_lock);
K
Kees Cook 已提交
170
static DEFINE_SPINLOCK(lock_me_up);
171

K
Kees Cook 已提交
172 173
static u8 data_area[EXEC_SIZE];

174
static const unsigned long rodata = 0xAA55AA55;
175
static unsigned long ro_after_init __ro_after_init = 0x55AA5500;
176

177
module_param(recur_count, int, 0644);
178
MODULE_PARM_DESC(recur_count, " Recursion level for the stack overflow test");
179
module_param(cpoint_name, charp, 0444);
180
MODULE_PARM_DESC(cpoint_name, " Crash Point, where kernel is to be crashed");
181
module_param(cpoint_type, charp, 0444);
182 183 184 185 186
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");
187

A
Adrian Bunk 已提交
188
static unsigned int jp_do_irq(unsigned int irq)
189 190 191 192 193 194
{
	lkdtm_handler();
	jprobe_return();
	return 0;
}

A
Adrian Bunk 已提交
195 196
static irqreturn_t jp_handle_irq_event(unsigned int irq,
				       struct irqaction *action)
197 198 199 200 201 202
{
	lkdtm_handler();
	jprobe_return();
	return 0;
}

A
Adrian Bunk 已提交
203
static void jp_tasklet_action(struct softirq_action *a)
204 205 206 207 208
{
	lkdtm_handler();
	jprobe_return();
}

A
Adrian Bunk 已提交
209
static void jp_ll_rw_block(int rw, int nr, struct buffer_head *bhs[])
210 211 212 213 214 215 216
{
	lkdtm_handler();
	jprobe_return();
}

struct scan_control;

A
Adrian Bunk 已提交
217 218 219
static unsigned long jp_shrink_inactive_list(unsigned long max_scan,
					     struct zone *zone,
					     struct scan_control *sc)
220 221 222 223 224 225
{
	lkdtm_handler();
	jprobe_return();
	return 0;
}

A
Adrian Bunk 已提交
226 227
static int jp_hrtimer_start(struct hrtimer *timer, ktime_t tim,
			    const enum hrtimer_mode mode)
228 229 230 231 232 233
{
	lkdtm_handler();
	jprobe_return();
	return 0;
}

A
Adrian Bunk 已提交
234
static int jp_scsi_dispatch_cmd(struct scsi_cmnd *cmd)
235 236 237 238 239 240 241
{
	lkdtm_handler();
	jprobe_return();
	return 0;
}

#ifdef CONFIG_IDE
242
static int jp_generic_ide_ioctl(ide_drive_t *drive, struct file *file,
243 244 245 246 247 248 249 250 251
			struct block_device *bdev, unsigned int cmd,
			unsigned long arg)
{
	lkdtm_handler();
	jprobe_return();
	return 0;
}
#endif

252 253 254 255 256 257 258 259 260 261
/* 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 已提交
262
	return CT_NONE;
263 264 265 266
}

static const char *cp_type_to_str(enum ctype type)
{
N
Namhyung Kim 已提交
267
	if (type == CT_NONE || type < 0 || type > ARRAY_SIZE(cp_type))
268 269 270 271 272 273 274
		return "None";

	return cp_type[type - 1];
}

static const char *cp_name_to_str(enum cname name)
{
N
Namhyung Kim 已提交
275
	if (name == CN_INVALID || name < 0 || name > ARRAY_SIZE(cp_name))
276 277 278 279 280 281
		return "INVALID";

	return cp_name[name - 1];
}


282 283 284
static int lkdtm_parse_commandline(void)
{
	int i;
285
	unsigned long flags;
286

287
	if (cpoint_count < 1 || recur_count < 1)
288 289
		return -EINVAL;

290
	spin_lock_irqsave(&count_lock, flags);
291
	count = cpoint_count;
292
	spin_unlock_irqrestore(&count_lock, flags);
293 294 295 296 297 298 299 300 301 302

	/* 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 已提交
303
	if (cptype == CT_NONE)
304 305 306
		return -EINVAL;

	for (i = 0; i < ARRAY_SIZE(cp_name); i++) {
307 308
		if (!strcmp(cpoint_name, cp_name[i])) {
			cpoint = i + 1;
309
			return 0;
310 311 312
		}
	}

313 314
	/* Could not find a valid crash point */
	return -EINVAL;
315 316
}

317
static int recursive_loop(int remaining)
318
{
319
	char buf[REC_STACK_SIZE];
320

321 322 323
	/* Make sure compiler does not optimize this away. */
	memset(buf, (remaining & 0xff) | 0x1, REC_STACK_SIZE);
	if (!remaining)
324 325
		return 0;
	else
326
		return recursive_loop(remaining - 1);
327 328
}

K
Kees Cook 已提交
329 330 331 332 333
static void do_nothing(void)
{
	return;
}

K
Kees Cook 已提交
334 335 336 337 338 339 340
/* Must immediately follow do_nothing for size calculuations to work out. */
static void do_overwritten(void)
{
	pr_info("do_overwritten wasn't overwritten!\n");
	return;
}

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

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

349
static void noinline execute_location(void *dst)
K
Kees Cook 已提交
350 351 352
{
	void (*func)(void) = dst;

353 354 355
	pr_info("attempting ok execution at %p\n", do_nothing);
	do_nothing();

K
Kees Cook 已提交
356
	memcpy(dst, do_nothing, EXEC_SIZE);
357 358
	flush_icache_range((unsigned long)dst, (unsigned long)dst + EXEC_SIZE);
	pr_info("attempting bad execution at %p\n", func);
K
Kees Cook 已提交
359 360 361
	func();
}

362 363
static void execute_user_location(void *dst)
{
K
Kees Cook 已提交
364
	/* Intentionally crossing kernel/user memory boundary. */
365 366
	void (*func)(void) = dst;

367 368 369
	pr_info("attempting ok execution at %p\n", do_nothing);
	do_nothing();

K
Kees Cook 已提交
370
	if (copy_to_user((void __user *)dst, do_nothing, EXEC_SIZE))
371
		return;
372 373
	flush_icache_range((unsigned long)dst, (unsigned long)dst + EXEC_SIZE);
	pr_info("attempting bad execution at %p\n", func);
374 375 376
	func();
}

377
static void lkdtm_do_action(enum ctype which)
378
{
379
	switch (which) {
N
Namhyung Kim 已提交
380
	case CT_PANIC:
381 382
		panic("dumptest");
		break;
N
Namhyung Kim 已提交
383
	case CT_BUG:
384 385
		BUG();
		break;
K
Kees Cook 已提交
386 387 388
	case CT_WARNING:
		WARN_ON(1);
		break;
N
Namhyung Kim 已提交
389
	case CT_EXCEPTION:
390 391
		*((int *) 0) = 0;
		break;
N
Namhyung Kim 已提交
392
	case CT_LOOP:
393 394 395
		for (;;)
			;
		break;
N
Namhyung Kim 已提交
396
	case CT_OVERFLOW:
397
		(void) recursive_loop(recur_count);
398
		break;
K
Kees Cook 已提交
399 400
	case CT_CORRUPT_STACK:
		corrupt_stack();
401
		break;
N
Namhyung Kim 已提交
402
	case CT_UNALIGNED_LOAD_STORE_WRITE: {
403 404 405 406 407 408 409 410 411 412 413
		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 已提交
414
	case CT_OVERWRITE_ALLOCATION: {
415 416 417 418 419 420 421
		size_t len = 1020;
		u32 *data = kmalloc(len, GFP_KERNEL);

		data[1024 / sizeof(u32)] = 0x12345678;
		kfree(data);
		break;
	}
N
Namhyung Kim 已提交
422
	case CT_WRITE_AFTER_FREE: {
K
Kees Cook 已提交
423
		int *base, *again;
424
		size_t len = 1024;
425 426 427 428 429 430
		/*
		 * 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;
431

432 433 434 435
		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 已提交
436
		kfree(base);
437
		base[offset] = 0x0abcdef0;
K
Kees Cook 已提交
438 439 440 441 442
		/* 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");
443 444 445

		break;
	}
L
Laura Abbott 已提交
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
	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 已提交
461 462
		if (!val) {
			kfree(base);
L
Laura Abbott 已提交
463
			break;
S
Sudip Mukherjee 已提交
464
		}
L
Laura Abbott 已提交
465 466 467 468 469 470 471 472 473 474 475

		*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 已提交
476
			pr_info("Memory correctly poisoned (%x)\n", saw);
L
Laura Abbott 已提交
477 478 479 480 481 482 483
			BUG();
		}
		pr_info("Memory was not poisoned\n");

		kfree(val);
		break;
	}
484 485 486 487 488 489 490
	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);
491
		schedule();
492 493
		pr_info("Attempting bad write to the buddy page after free\n");
		memset((void *)p, 0x78, PAGE_SIZE);
K
Kees Cook 已提交
494 495 496
		/* Attempt to notice the overwrite. */
		p = __get_free_page(GFP_KERNEL);
		free_page(p);
497
		schedule();
K
Kees Cook 已提交
498

499 500 501 502
		break;
	}
	case CT_READ_BUDDY_AFTER_FREE: {
		unsigned long p = __get_free_page(GFP_KERNEL);
S
Sudip Mukherjee 已提交
503
		int saw, *val;
504 505 506 507 508
		int *base;

		if (!p)
			break;

S
Sudip Mukherjee 已提交
509
		val = kmalloc(1024, GFP_KERNEL);
510 511
		if (!val) {
			free_page(p);
512
			break;
513
		}
514 515 516 517 518 519 520 521 522 523 524

		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 已提交
525
			pr_info("Memory correctly poisoned (%x)\n", saw);
526 527 528 529 530
			BUG();
		}
		pr_info("Buddy page was not poisoned\n");

		kfree(val);
531 532
		break;
	}
N
Namhyung Kim 已提交
533
	case CT_SOFTLOCKUP:
534 535 536 537
		preempt_disable();
		for (;;)
			cpu_relax();
		break;
N
Namhyung Kim 已提交
538
	case CT_HARDLOCKUP:
539 540 541 542
		local_irq_disable();
		for (;;)
			cpu_relax();
		break;
K
Kees Cook 已提交
543 544 545
	case CT_SPINLOCKUP:
		/* Must be called twice to trigger. */
		spin_lock(&lock_me_up);
K
Kees Cook 已提交
546 547
		/* Let sparse know we intended to exit holding the lock. */
		__release(&lock_me_up);
K
Kees Cook 已提交
548
		break;
N
Namhyung Kim 已提交
549
	case CT_HUNG_TASK:
550 551 552
		set_current_state(TASK_UNINTERRUPTIBLE);
		schedule();
		break;
K
Kees Cook 已提交
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572
	case CT_EXEC_DATA:
		execute_location(data_area);
		break;
	case CT_EXEC_STACK: {
		u8 stack_area[EXEC_SIZE];
		execute_location(stack_area);
		break;
	}
	case CT_EXEC_KMALLOC: {
		u32 *kmalloc_area = kmalloc(EXEC_SIZE, GFP_KERNEL);
		execute_location(kmalloc_area);
		kfree(kmalloc_area);
		break;
	}
	case CT_EXEC_VMALLOC: {
		u32 *vmalloc_area = vmalloc(EXEC_SIZE);
		execute_location(vmalloc_area);
		vfree(vmalloc_area);
		break;
	}
573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
	case CT_EXEC_USERSPACE: {
		unsigned long user_addr;

		user_addr = vm_mmap(NULL, 0, PAGE_SIZE,
				    PROT_READ | PROT_WRITE | PROT_EXEC,
				    MAP_ANONYMOUS | MAP_PRIVATE, 0);
		if (user_addr >= TASK_SIZE) {
			pr_warn("Failed to allocate user memory\n");
			return;
		}
		execute_user_location((void *)user_addr);
		vm_munmap(user_addr, PAGE_SIZE);
		break;
	}
	case CT_ACCESS_USERSPACE: {
S
Stephen Smalley 已提交
588
		unsigned long user_addr, tmp = 0;
589 590 591 592 593 594 595 596 597 598
		unsigned long *ptr;

		user_addr = vm_mmap(NULL, 0, PAGE_SIZE,
				    PROT_READ | PROT_WRITE | PROT_EXEC,
				    MAP_ANONYMOUS | MAP_PRIVATE, 0);
		if (user_addr >= TASK_SIZE) {
			pr_warn("Failed to allocate user memory\n");
			return;
		}

S
Stephen Smalley 已提交
599 600 601 602 603 604
		if (copy_to_user((void __user *)user_addr, &tmp, sizeof(tmp))) {
			pr_warn("copy_to_user failed\n");
			vm_munmap(user_addr, PAGE_SIZE);
			return;
		}

605
		ptr = (unsigned long *)user_addr;
606 607

		pr_info("attempting bad read at %p\n", ptr);
608 609
		tmp = *ptr;
		tmp += 0xc0dec0de;
610 611

		pr_info("attempting bad write at %p\n", ptr);
612 613 614 615 616 617 618
		*ptr = tmp;

		vm_munmap(user_addr, PAGE_SIZE);

		break;
	}
	case CT_WRITE_RO: {
619 620
		/* Explicitly cast away "const" for the test. */
		unsigned long *ptr = (unsigned long *)&rodata;
621

622 623
		pr_info("attempting bad rodata write at %p\n", ptr);
		*ptr ^= 0xabcd1234;
624

625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640
		break;
	}
	case CT_WRITE_RO_AFTER_INIT: {
		unsigned long *ptr = &ro_after_init;

		/*
		 * Verify we were written to during init. Since an Oops
		 * is considered a "success", a failure is to just skip the
		 * real test.
		 */
		if ((*ptr & 0xAA) != 0xAA) {
			pr_info("%p was NOT written during init!?\n", ptr);
			break;
		}

		pr_info("attempting bad ro_after_init write at %p\n", ptr);
641 642 643 644
		*ptr ^= 0xabcd1234;

		break;
	}
K
Kees Cook 已提交
645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660
	case CT_WRITE_KERN: {
		size_t size;
		unsigned char *ptr;

		size = (unsigned long)do_overwritten -
		       (unsigned long)do_nothing;
		ptr = (unsigned char *)do_overwritten;

		pr_info("attempting bad %zu byte write at %p\n", size, ptr);
		memcpy(ptr, (unsigned char *)do_nothing, size);
		flush_icache_range((unsigned long)ptr,
				   (unsigned long)(ptr + size));

		do_overwritten();
		break;
	}
661 662 663 664 665 666 667 668 669 670 671
	case CT_WRAP_ATOMIC: {
		atomic_t under = ATOMIC_INIT(INT_MIN);
		atomic_t over = ATOMIC_INIT(INT_MAX);

		pr_info("attempting atomic underflow\n");
		atomic_dec(&under);
		pr_info("attempting atomic overflow\n");
		atomic_inc(&over);

		return;
	}
N
Namhyung Kim 已提交
672
	case CT_NONE:
673 674 675 676 677 678 679 680
	default:
		break;
	}

}

static void lkdtm_handler(void)
{
681
	unsigned long flags;
682
	bool do_it = false;
683 684

	spin_lock_irqsave(&count_lock, flags);
685
	count--;
686 687
	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);
688 689

	if (count == 0) {
690
		do_it = true;
691 692
		count = cpoint_count;
	}
693
	spin_unlock_irqrestore(&count_lock, flags);
694 695 696

	if (do_it)
		lkdtm_do_action(cptype);
697 698
}

699
static int lkdtm_register_cpoint(enum cname which)
700 701 702
{
	int ret;

N
Namhyung Kim 已提交
703
	cpoint = CN_INVALID;
704 705
	if (lkdtm.entry != NULL)
		unregister_jprobe(&lkdtm);
706

707
	switch (which) {
N
Namhyung Kim 已提交
708
	case CN_DIRECT:
709 710
		lkdtm_do_action(cptype);
		return 0;
N
Namhyung Kim 已提交
711
	case CN_INT_HARDWARE_ENTRY:
M
M. Mohan Kumar 已提交
712
		lkdtm.kp.symbol_name = "do_IRQ";
713 714
		lkdtm.entry = (kprobe_opcode_t*) jp_do_irq;
		break;
N
Namhyung Kim 已提交
715
	case CN_INT_HW_IRQ_EN:
716 717 718
		lkdtm.kp.symbol_name = "handle_IRQ_event";
		lkdtm.entry = (kprobe_opcode_t*) jp_handle_irq_event;
		break;
N
Namhyung Kim 已提交
719
	case CN_INT_TASKLET_ENTRY:
720 721 722
		lkdtm.kp.symbol_name = "tasklet_action";
		lkdtm.entry = (kprobe_opcode_t*) jp_tasklet_action;
		break;
N
Namhyung Kim 已提交
723
	case CN_FS_DEVRW:
724 725 726
		lkdtm.kp.symbol_name = "ll_rw_block";
		lkdtm.entry = (kprobe_opcode_t*) jp_ll_rw_block;
		break;
N
Namhyung Kim 已提交
727
	case CN_MEM_SWAPOUT:
728 729
		lkdtm.kp.symbol_name = "shrink_inactive_list";
		lkdtm.entry = (kprobe_opcode_t*) jp_shrink_inactive_list;
730
		break;
N
Namhyung Kim 已提交
731
	case CN_TIMERADD:
732 733 734
		lkdtm.kp.symbol_name = "hrtimer_start";
		lkdtm.entry = (kprobe_opcode_t*) jp_hrtimer_start;
		break;
N
Namhyung Kim 已提交
735
	case CN_SCSI_DISPATCH_CMD:
736 737 738
		lkdtm.kp.symbol_name = "scsi_dispatch_cmd";
		lkdtm.entry = (kprobe_opcode_t*) jp_scsi_dispatch_cmd;
		break;
N
Namhyung Kim 已提交
739
	case CN_IDE_CORE_CP:
740 741 742 743
#ifdef CONFIG_IDE
		lkdtm.kp.symbol_name = "generic_ide_ioctl";
		lkdtm.entry = (kprobe_opcode_t*) jp_generic_ide_ioctl;
#else
744
		pr_info("Crash point not available\n");
745
		return -EINVAL;
746 747 748
#endif
		break;
	default:
749
		pr_info("Invalid Crash Point\n");
750
		return -EINVAL;
751 752
	}

753
	cpoint = which;
754
	if ((ret = register_jprobe(&lkdtm)) < 0) {
755
		pr_info("Couldn't register jprobe\n");
N
Namhyung Kim 已提交
756
		cpoint = CN_INVALID;
757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784
	}

	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 已提交
785
	if (cptype == CT_NONE)
786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804
		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);
805 806
	if (buf == NULL)
		return -ENOMEM;
807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828

	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 已提交
829
	return do_register_entry(CN_INT_HARDWARE_ENTRY, f, buf, count, off);
830 831 832 833 834
}

static ssize_t int_hw_irq_en(struct file *f, const char __user *buf,
		size_t count, loff_t *off)
{
N
Namhyung Kim 已提交
835
	return do_register_entry(CN_INT_HW_IRQ_EN, f, buf, count, off);
836 837 838 839 840
}

static ssize_t int_tasklet_entry(struct file *f, const char __user *buf,
		size_t count, loff_t *off)
{
N
Namhyung Kim 已提交
841
	return do_register_entry(CN_INT_TASKLET_ENTRY, f, buf, count, off);
842 843 844 845 846
}

static ssize_t fs_devrw_entry(struct file *f, const char __user *buf,
		size_t count, loff_t *off)
{
N
Namhyung Kim 已提交
847
	return do_register_entry(CN_FS_DEVRW, f, buf, count, off);
848 849 850 851 852
}

static ssize_t mem_swapout_entry(struct file *f, const char __user *buf,
		size_t count, loff_t *off)
{
N
Namhyung Kim 已提交
853
	return do_register_entry(CN_MEM_SWAPOUT, f, buf, count, off);
854 855 856 857 858
}

static ssize_t timeradd_entry(struct file *f, const char __user *buf,
		size_t count, loff_t *off)
{
N
Namhyung Kim 已提交
859
	return do_register_entry(CN_TIMERADD, f, buf, count, off);
860 861 862 863 864
}

static ssize_t scsi_dispatch_cmd_entry(struct file *f,
		const char __user *buf, size_t count, loff_t *off)
{
N
Namhyung Kim 已提交
865
	return do_register_entry(CN_SCSI_DISPATCH_CMD, f, buf, count, off);
866 867 868 869 870
}

static ssize_t ide_core_cp_entry(struct file *f, const char __user *buf,
		size_t count, loff_t *off)
{
N
Namhyung Kim 已提交
871
	return do_register_entry(CN_IDE_CORE_CP, f, buf, count, off);
872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898
}

/* 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 已提交
899
	if (type == CT_NONE)
900 901
		return -EINVAL;

902
	pr_info("Performing direct entry %s\n", cp_type_to_str(type));
903 904 905 906 907 908 909 910 911 912 913 914 915
	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,
916
			.llseek = generic_file_llseek,
917 918 919
			.open = lkdtm_debugfs_open,
			.write = direct_entry} },
	{"INT_HARDWARE_ENTRY", {.read = lkdtm_debugfs_read,
920
			.llseek = generic_file_llseek,
921 922 923
			.open = lkdtm_debugfs_open,
			.write = int_hardware_entry} },
	{"INT_HW_IRQ_EN", {.read = lkdtm_debugfs_read,
924
			.llseek = generic_file_llseek,
925 926 927
			.open = lkdtm_debugfs_open,
			.write = int_hw_irq_en} },
	{"INT_TASKLET_ENTRY", {.read = lkdtm_debugfs_read,
928
			.llseek = generic_file_llseek,
929 930 931
			.open = lkdtm_debugfs_open,
			.write = int_tasklet_entry} },
	{"FS_DEVRW", {.read = lkdtm_debugfs_read,
932
			.llseek = generic_file_llseek,
933 934 935
			.open = lkdtm_debugfs_open,
			.write = fs_devrw_entry} },
	{"MEM_SWAPOUT", {.read = lkdtm_debugfs_read,
936
			.llseek = generic_file_llseek,
937 938 939
			.open = lkdtm_debugfs_open,
			.write = mem_swapout_entry} },
	{"TIMERADD", {.read = lkdtm_debugfs_read,
940
			.llseek = generic_file_llseek,
941 942 943
			.open = lkdtm_debugfs_open,
			.write = timeradd_entry} },
	{"SCSI_DISPATCH_CMD", {.read = lkdtm_debugfs_read,
944
			.llseek = generic_file_llseek,
945 946 947
			.open = lkdtm_debugfs_open,
			.write = scsi_dispatch_cmd_entry} },
	{"IDE_CORE_CP",	{.read = lkdtm_debugfs_read,
948
			.llseek = generic_file_llseek,
949 950 951 952 953 954 955 956 957 958 959 960
			.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;

961 962 963
	/* Make sure we can write to __ro_after_init values during __init */
	ro_after_init |= 0xAA;

964 965 966
	/* Register debugfs interface */
	lkdtm_debugfs_root = debugfs_create_dir("provoke-crash", NULL);
	if (!lkdtm_debugfs_root) {
967
		pr_err("creating root dir failed\n");
968 969 970 971 972 973 974 975 976 977 978 979 980 981
		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) {
982
			pr_err("could not create %s\n", cur->name);
983 984 985 986 987
			goto out_err;
		}
	}

	if (lkdtm_parse_commandline() == -EINVAL) {
988
		pr_info("Invalid command\n");
989 990 991
		goto out_err;
	}

N
Namhyung Kim 已提交
992
	if (cpoint != CN_INVALID && cptype != CT_NONE) {
993 994
		ret = lkdtm_register_cpoint(cpoint);
		if (ret < 0) {
995
			pr_info("Invalid crash point %d\n", cpoint);
996 997
			goto out_err;
		}
998 999
		pr_info("Crash point %s of type %s registered\n",
			cpoint_name, cpoint_type);
1000
	} else {
1001
		pr_info("No crash points registered, enable through debugfs\n");
1002 1003 1004
	}

	return 0;
1005 1006 1007 1008

out_err:
	debugfs_remove_recursive(lkdtm_debugfs_root);
	return ret;
1009 1010
}

A
Adrian Bunk 已提交
1011
static void __exit lkdtm_module_exit(void)
1012
{
1013 1014 1015
	debugfs_remove_recursive(lkdtm_debugfs_root);

	unregister_jprobe(&lkdtm);
1016
	pr_info("Crash point unregistered\n");
1017 1018 1019 1020 1021 1022
}

module_init(lkdtm_module_init);
module_exit(lkdtm_module_exit);

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