test_nx.c 4.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
/*
 * test_nx.c: functional test for NX functionality
 *
 * (C) Copyright 2008 Intel Corporation
 * Author: Arjan van de Ven <arjan@linux.intel.com>
 *
 * 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; version 2
 * of the License.
 */
#include <linux/module.h>
#include <linux/sort.h>
14 15
#include <linux/slab.h>

16
#include <asm/uaccess.h>
17
#include <asm/asm.h>
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94

extern int rodata_test_data;

/*
 * This file checks 4 things:
 * 1) Check if the stack is not executable
 * 2) Check if kmalloc memory is not executable
 * 3) Check if the .rodata section is not executable
 * 4) Check if the .data section of a module is not executable
 *
 * To do this, the test code tries to execute memory in stack/kmalloc/etc,
 * and then checks if the expected trap happens.
 *
 * Sadly, this implies having a dynamic exception handling table entry.
 * ... which can be done (and will make Rusty cry)... but it can only
 * be done in a stand-alone module with only 1 entry total.
 * (otherwise we'd have to sort and that's just too messy)
 */



/*
 * We want to set up an exception handling point on our stack,
 * which means a variable value. This function is rather dirty
 * and walks the exception table of the module, looking for a magic
 * marker and replaces it with a specific function.
 */
static void fudze_exception_table(void *marker, void *new)
{
	struct module *mod = THIS_MODULE;
	struct exception_table_entry *extable;

	/*
	 * Note: This module has only 1 exception table entry,
	 * so searching and sorting is not needed. If that changes,
	 * this would be the place to search and re-sort the exception
	 * table.
	 */
	if (mod->num_exentries > 1) {
		printk(KERN_ERR "test_nx: too many exception table entries!\n");
		printk(KERN_ERR "test_nx: test results are not reliable.\n");
		return;
	}
	extable = (struct exception_table_entry *)mod->extable;
	extable[0].insn = (unsigned long)new;
}


/*
 * exception tables get their symbols translated so we need
 * to use a fake function to put in there, which we can then
 * replace at runtime.
 */
void foo_label(void);

/*
 * returns 0 for not-executable, negative for executable
 *
 * Note: we cannot allow this function to be inlined, because
 * that would give us more than 1 exception table entry.
 * This in turn would break the assumptions above.
 */
static noinline int test_address(void *address)
{
	unsigned long result;

	/* Set up an exception table entry for our address */
	fudze_exception_table(&foo_label, address);
	result = 1;
	asm volatile(
		"foo_label:\n"
		"0:	call *%[fake_code]\n"
		"1:\n"
		".section .fixup,\"ax\"\n"
		"2:	mov %[zero], %[rslt]\n"
		"	ret\n"
		".previous\n"
95
		_ASM_EXTABLE(0b,2b)
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
		: [rslt] "=r" (result)
		: [fake_code] "r" (address), [zero] "r" (0UL), "0" (result)
	);
	/* change the exception table back for the next round */
	fudze_exception_table(address, &foo_label);

	if (result)
		return -ENODEV;
	return 0;
}

static unsigned char test_data = 0xC3; /* 0xC3 is the opcode for "ret" */

static int test_NX(void)
{
	int ret = 0;
	/* 0xC3 is the opcode for "ret" */
	char stackcode[] = {0xC3, 0x90, 0 };
	char *heap;

	test_data = 0xC3;

	printk(KERN_INFO "Testing NX protection\n");

	/* Test 1: check if the stack is not executable */
	if (test_address(&stackcode)) {
		printk(KERN_ERR "test_nx: stack was executable\n");
		ret = -ENODEV;
	}


	/* Test 2: Check if the heap is executable */
	heap = kmalloc(64, GFP_KERNEL);
	if (!heap)
		return -ENOMEM;
	heap[0] = 0xC3; /* opcode for "ret" */

	if (test_address(heap)) {
		printk(KERN_ERR "test_nx: heap was executable\n");
		ret = -ENODEV;
	}
	kfree(heap);

	/*
	 * The following 2 tests currently fail, this needs to get fixed
	 * Until then, don't run them to avoid too many people getting scared
	 * by the error message
	 */

#ifdef CONFIG_DEBUG_RODATA
	/* Test 3: Check if the .rodata section is executable */
	if (rodata_test_data != 0xC3) {
		printk(KERN_ERR "test_nx: .rodata marker has invalid value\n");
		ret = -ENODEV;
	} else if (test_address(&rodata_test_data)) {
		printk(KERN_ERR "test_nx: .rodata section is executable\n");
		ret = -ENODEV;
	}
#endif

156
#if 0
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
	/* Test 4: Check if the .data section of a module is executable */
	if (test_address(&test_data)) {
		printk(KERN_ERR "test_nx: .data section is executable\n");
		ret = -ENODEV;
	}

#endif
	return 0;
}

static void test_exit(void)
{
}

module_init(test_NX);
module_exit(test_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Testcase for the NX infrastructure");
MODULE_AUTHOR("Arjan van de Ven <arjan@linux.intel.com>");