lkdtm_core.c 23.5 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
#include "lkdtm.h"

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

69
#define DEFAULT_COUNT 10
K
Kees Cook 已提交
70
#define EXEC_SIZE 64
71 72

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

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

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

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

static struct jprobe lkdtm;

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

165 166
static char* cpoint_name;
static char* cpoint_type;
167 168 169
static int cpoint_count = DEFAULT_COUNT;
static int recur_count = REC_NUM_DEFAULT;

N
Namhyung Kim 已提交
170 171
static enum cname cpoint = CN_INVALID;
static enum ctype cptype = CT_NONE;
172
static int count = DEFAULT_COUNT;
173
static DEFINE_SPINLOCK(count_lock);
K
Kees Cook 已提交
174
static DEFINE_SPINLOCK(lock_me_up);
175

K
Kees Cook 已提交
176 177
static u8 data_area[EXEC_SIZE];

178
static const unsigned long rodata = 0xAA55AA55;
179
static unsigned long ro_after_init __ro_after_init = 0x55AA5500;
180

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

A
Adrian Bunk 已提交
192
static unsigned int jp_do_irq(unsigned int irq)
193 194 195 196 197 198
{
	lkdtm_handler();
	jprobe_return();
	return 0;
}

A
Adrian Bunk 已提交
199 200
static irqreturn_t jp_handle_irq_event(unsigned int irq,
				       struct irqaction *action)
201 202 203 204 205 206
{
	lkdtm_handler();
	jprobe_return();
	return 0;
}

A
Adrian Bunk 已提交
207
static void jp_tasklet_action(struct softirq_action *a)
208 209 210 211 212
{
	lkdtm_handler();
	jprobe_return();
}

A
Adrian Bunk 已提交
213
static void jp_ll_rw_block(int rw, int nr, struct buffer_head *bhs[])
214 215 216 217 218 219 220
{
	lkdtm_handler();
	jprobe_return();
}

struct scan_control;

A
Adrian Bunk 已提交
221 222 223
static unsigned long jp_shrink_inactive_list(unsigned long max_scan,
					     struct zone *zone,
					     struct scan_control *sc)
224 225 226 227 228 229
{
	lkdtm_handler();
	jprobe_return();
	return 0;
}

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

A
Adrian Bunk 已提交
238
static int jp_scsi_dispatch_cmd(struct scsi_cmnd *cmd)
239 240 241 242 243 244 245
{
	lkdtm_handler();
	jprobe_return();
	return 0;
}

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

256 257 258 259 260 261 262 263 264 265
/* 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 已提交
266
	return CT_NONE;
267 268 269 270
}

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

	return cp_type[type - 1];
}

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

	return cp_name[name - 1];
}


286 287 288
static int lkdtm_parse_commandline(void)
{
	int i;
289
	unsigned long flags;
290

291
	if (cpoint_count < 1 || recur_count < 1)
292 293
		return -EINVAL;

294
	spin_lock_irqsave(&count_lock, flags);
295
	count = cpoint_count;
296
	spin_unlock_irqrestore(&count_lock, flags);
297 298 299 300 301 302 303 304 305 306

	/* 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 已提交
307
	if (cptype == CT_NONE)
308 309 310
		return -EINVAL;

	for (i = 0; i < ARRAY_SIZE(cp_name); i++) {
311 312
		if (!strcmp(cpoint_name, cp_name[i])) {
			cpoint = i + 1;
313
			return 0;
314 315 316
		}
	}

317 318
	/* Could not find a valid crash point */
	return -EINVAL;
319 320
}

321
static int recursive_loop(int remaining)
322
{
323
	char buf[REC_STACK_SIZE];
324

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

K
Kees Cook 已提交
333 334 335 336 337
static void do_nothing(void)
{
	return;
}

K
Kees Cook 已提交
338 339 340 341 342 343 344
/* 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 已提交
345 346 347 348 349 350 351 352
static noinline void corrupt_stack(void)
{
	/* Use default char array length that triggers stack protection. */
	char data[8];

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

353
static noinline void execute_location(void *dst, bool write)
K
Kees Cook 已提交
354 355 356
{
	void (*func)(void) = dst;

357 358 359
	pr_info("attempting ok execution at %p\n", do_nothing);
	do_nothing();

360 361 362 363 364
	if (write) {
		memcpy(dst, do_nothing, EXEC_SIZE);
		flush_icache_range((unsigned long)dst,
				   (unsigned long)dst + EXEC_SIZE);
	}
365
	pr_info("attempting bad execution at %p\n", func);
K
Kees Cook 已提交
366 367 368
	func();
}

369 370
static void execute_user_location(void *dst)
{
K
Kees Cook 已提交
371
	/* Intentionally crossing kernel/user memory boundary. */
372 373
	void (*func)(void) = dst;

374 375 376
	pr_info("attempting ok execution at %p\n", do_nothing);
	do_nothing();

K
Kees Cook 已提交
377
	if (copy_to_user((void __user *)dst, do_nothing, EXEC_SIZE))
378
		return;
379 380
	flush_icache_range((unsigned long)dst, (unsigned long)dst + EXEC_SIZE);
	pr_info("attempting bad execution at %p\n", func);
381 382 383
	func();
}

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

		data[1024 / sizeof(u32)] = 0x12345678;
		kfree(data);
		break;
	}
N
Namhyung Kim 已提交
429
	case CT_WRITE_AFTER_FREE: {
K
Kees Cook 已提交
430
		int *base, *again;
431
		size_t len = 1024;
432 433 434 435 436 437
		/*
		 * 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;
438

439 440 441 442
		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 已提交
443
		kfree(base);
444
		base[offset] = 0x0abcdef0;
K
Kees Cook 已提交
445 446 447 448 449
		/* 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");
450 451 452

		break;
	}
L
Laura Abbott 已提交
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
	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 已提交
468 469
		if (!val) {
			kfree(base);
L
Laura Abbott 已提交
470
			break;
S
Sudip Mukherjee 已提交
471
		}
L
Laura Abbott 已提交
472 473 474 475 476 477 478 479 480 481 482

		*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 已提交
483
			pr_info("Memory correctly poisoned (%x)\n", saw);
L
Laura Abbott 已提交
484 485 486 487 488 489 490
			BUG();
		}
		pr_info("Memory was not poisoned\n");

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

506 507 508 509
		break;
	}
	case CT_READ_BUDDY_AFTER_FREE: {
		unsigned long p = __get_free_page(GFP_KERNEL);
S
Sudip Mukherjee 已提交
510
		int saw, *val;
511 512 513 514 515
		int *base;

		if (!p)
			break;

S
Sudip Mukherjee 已提交
516
		val = kmalloc(1024, GFP_KERNEL);
517 518
		if (!val) {
			free_page(p);
519
			break;
520
		}
521 522 523 524 525 526 527 528 529 530 531

		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 已提交
532
			pr_info("Memory correctly poisoned (%x)\n", saw);
533 534 535 536 537
			BUG();
		}
		pr_info("Buddy page was not poisoned\n");

		kfree(val);
538 539
		break;
	}
N
Namhyung Kim 已提交
540
	case CT_SOFTLOCKUP:
541 542 543 544
		preempt_disable();
		for (;;)
			cpu_relax();
		break;
N
Namhyung Kim 已提交
545
	case CT_HARDLOCKUP:
546 547 548 549
		local_irq_disable();
		for (;;)
			cpu_relax();
		break;
K
Kees Cook 已提交
550 551 552
	case CT_SPINLOCKUP:
		/* Must be called twice to trigger. */
		spin_lock(&lock_me_up);
K
Kees Cook 已提交
553 554
		/* Let sparse know we intended to exit holding the lock. */
		__release(&lock_me_up);
K
Kees Cook 已提交
555
		break;
N
Namhyung Kim 已提交
556
	case CT_HUNG_TASK:
557 558 559
		set_current_state(TASK_UNINTERRUPTIBLE);
		schedule();
		break;
K
Kees Cook 已提交
560
	case CT_EXEC_DATA:
561
		execute_location(data_area, true);
K
Kees Cook 已提交
562 563 564
		break;
	case CT_EXEC_STACK: {
		u8 stack_area[EXEC_SIZE];
565
		execute_location(stack_area, true);
K
Kees Cook 已提交
566 567 568 569
		break;
	}
	case CT_EXEC_KMALLOC: {
		u32 *kmalloc_area = kmalloc(EXEC_SIZE, GFP_KERNEL);
570
		execute_location(kmalloc_area, true);
K
Kees Cook 已提交
571 572 573 574 575
		kfree(kmalloc_area);
		break;
	}
	case CT_EXEC_VMALLOC: {
		u32 *vmalloc_area = vmalloc(EXEC_SIZE);
576
		execute_location(vmalloc_area, true);
K
Kees Cook 已提交
577 578 579
		vfree(vmalloc_area);
		break;
	}
580 581 582
	case CT_EXEC_RODATA:
		execute_location(lkdtm_rodata_do_nothing, false);
		break;
583 584 585 586 587 588 589 590 591 592 593 594 595 596 597
	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 已提交
598
		unsigned long user_addr, tmp = 0;
599 600 601 602 603 604 605 606 607 608
		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 已提交
609 610 611 612 613 614
		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;
		}

615
		ptr = (unsigned long *)user_addr;
616 617

		pr_info("attempting bad read at %p\n", ptr);
618 619
		tmp = *ptr;
		tmp += 0xc0dec0de;
620 621

		pr_info("attempting bad write at %p\n", ptr);
622 623 624 625 626 627 628
		*ptr = tmp;

		vm_munmap(user_addr, PAGE_SIZE);

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

632 633
		pr_info("attempting bad rodata write at %p\n", ptr);
		*ptr ^= 0xabcd1234;
634

635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650
		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);
651 652 653 654
		*ptr ^= 0xabcd1234;

		break;
	}
K
Kees Cook 已提交
655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670
	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;
	}
671 672 673 674 675 676 677 678 679 680 681
	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 已提交
682
	case CT_NONE:
683 684 685 686 687 688 689 690
	default:
		break;
	}

}

static void lkdtm_handler(void)
{
691
	unsigned long flags;
692
	bool do_it = false;
693 694

	spin_lock_irqsave(&count_lock, flags);
695
	count--;
696 697
	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);
698 699

	if (count == 0) {
700
		do_it = true;
701 702
		count = cpoint_count;
	}
703
	spin_unlock_irqrestore(&count_lock, flags);
704 705 706

	if (do_it)
		lkdtm_do_action(cptype);
707 708
}

709
static int lkdtm_register_cpoint(enum cname which)
710 711 712
{
	int ret;

N
Namhyung Kim 已提交
713
	cpoint = CN_INVALID;
714 715
	if (lkdtm.entry != NULL)
		unregister_jprobe(&lkdtm);
716

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

763
	cpoint = which;
764
	if ((ret = register_jprobe(&lkdtm)) < 0) {
765
		pr_info("Couldn't register jprobe\n");
N
Namhyung Kim 已提交
766
		cpoint = CN_INVALID;
767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794
	}

	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 已提交
795
	if (cptype == CT_NONE)
796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814
		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);
815 816
	if (buf == NULL)
		return -ENOMEM;
817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838

	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 已提交
839
	return do_register_entry(CN_INT_HARDWARE_ENTRY, f, buf, count, off);
840 841 842 843 844
}

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

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

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

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

static ssize_t timeradd_entry(struct file *f, const char __user *buf,
		size_t count, loff_t *off)
{
N
Namhyung Kim 已提交
869
	return do_register_entry(CN_TIMERADD, f, buf, count, off);
870 871 872 873 874
}

static ssize_t scsi_dispatch_cmd_entry(struct file *f,
		const char __user *buf, size_t count, loff_t *off)
{
N
Namhyung Kim 已提交
875
	return do_register_entry(CN_SCSI_DISPATCH_CMD, f, buf, count, off);
876 877 878 879 880
}

static ssize_t ide_core_cp_entry(struct file *f, const char __user *buf,
		size_t count, loff_t *off)
{
N
Namhyung Kim 已提交
881
	return do_register_entry(CN_IDE_CORE_CP, f, buf, count, off);
882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908
}

/* 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 已提交
909
	if (type == CT_NONE)
910 911
		return -EINVAL;

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

971 972 973
	/* Make sure we can write to __ro_after_init values during __init */
	ro_after_init |= 0xAA;

974 975 976
	/* Register debugfs interface */
	lkdtm_debugfs_root = debugfs_create_dir("provoke-crash", NULL);
	if (!lkdtm_debugfs_root) {
977
		pr_err("creating root dir failed\n");
978 979 980 981 982 983 984 985 986 987 988 989 990 991
		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) {
992
			pr_err("could not create %s\n", cur->name);
993 994 995 996 997
			goto out_err;
		}
	}

	if (lkdtm_parse_commandline() == -EINVAL) {
998
		pr_info("Invalid command\n");
999 1000 1001
		goto out_err;
	}

N
Namhyung Kim 已提交
1002
	if (cpoint != CN_INVALID && cptype != CT_NONE) {
1003 1004
		ret = lkdtm_register_cpoint(cpoint);
		if (ret < 0) {
1005
			pr_info("Invalid crash point %d\n", cpoint);
1006 1007
			goto out_err;
		}
1008 1009
		pr_info("Crash point %s of type %s registered\n",
			cpoint_name, cpoint_type);
1010
	} else {
1011
		pr_info("No crash points registered, enable through debugfs\n");
1012 1013 1014
	}

	return 0;
1015 1016 1017 1018

out_err:
	debugfs_remove_recursive(lkdtm_debugfs_root);
	return ret;
1019 1020
}

A
Adrian Bunk 已提交
1021
static void __exit lkdtm_module_exit(void)
1022
{
1023 1024 1025
	debugfs_remove_recursive(lkdtm_debugfs_root);

	unregister_jprobe(&lkdtm);
1026
	pr_info("Crash point unregistered\n");
1027 1028 1029 1030 1031 1032
}

module_init(lkdtm_module_init);
module_exit(lkdtm_module_exit);

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