Arjan van de Ven | edeed30 | 2008-01-30 13:34:08 +0100 | [diff] [blame] | 1 | /* |
| 2 | * test_nx.c: functional test for NX functionality |
| 3 | * |
| 4 | * (C) Copyright 2008 Intel Corporation |
| 5 | * Author: Arjan van de Ven <arjan@linux.intel.com> |
| 6 | * |
| 7 | * This program is free software; you can redistribute it and/or |
| 8 | * modify it under the terms of the GNU General Public License |
| 9 | * as published by the Free Software Foundation; version 2 |
| 10 | * of the License. |
| 11 | */ |
| 12 | #include <linux/module.h> |
| 13 | #include <linux/sort.h> |
Robert P. J. Day | 0054f4b | 2008-03-13 21:47:32 -0400 | [diff] [blame] | 14 | #include <linux/slab.h> |
| 15 | |
Arjan van de Ven | edeed30 | 2008-01-30 13:34:08 +0100 | [diff] [blame] | 16 | #include <asm/uaccess.h> |
H. Peter Anvin | 9290909 | 2008-02-04 16:47:56 +0100 | [diff] [blame] | 17 | #include <asm/asm.h> |
Arjan van de Ven | edeed30 | 2008-01-30 13:34:08 +0100 | [diff] [blame] | 18 | |
| 19 | extern int rodata_test_data; |
| 20 | |
| 21 | /* |
| 22 | * This file checks 4 things: |
| 23 | * 1) Check if the stack is not executable |
| 24 | * 2) Check if kmalloc memory is not executable |
| 25 | * 3) Check if the .rodata section is not executable |
| 26 | * 4) Check if the .data section of a module is not executable |
| 27 | * |
| 28 | * To do this, the test code tries to execute memory in stack/kmalloc/etc, |
| 29 | * and then checks if the expected trap happens. |
| 30 | * |
| 31 | * Sadly, this implies having a dynamic exception handling table entry. |
| 32 | * ... which can be done (and will make Rusty cry)... but it can only |
| 33 | * be done in a stand-alone module with only 1 entry total. |
| 34 | * (otherwise we'd have to sort and that's just too messy) |
| 35 | */ |
| 36 | |
| 37 | |
| 38 | |
| 39 | /* |
| 40 | * We want to set up an exception handling point on our stack, |
| 41 | * which means a variable value. This function is rather dirty |
| 42 | * and walks the exception table of the module, looking for a magic |
| 43 | * marker and replaces it with a specific function. |
| 44 | */ |
| 45 | static void fudze_exception_table(void *marker, void *new) |
| 46 | { |
| 47 | struct module *mod = THIS_MODULE; |
| 48 | struct exception_table_entry *extable; |
| 49 | |
| 50 | /* |
| 51 | * Note: This module has only 1 exception table entry, |
| 52 | * so searching and sorting is not needed. If that changes, |
| 53 | * this would be the place to search and re-sort the exception |
| 54 | * table. |
| 55 | */ |
| 56 | if (mod->num_exentries > 1) { |
| 57 | printk(KERN_ERR "test_nx: too many exception table entries!\n"); |
| 58 | printk(KERN_ERR "test_nx: test results are not reliable.\n"); |
| 59 | return; |
| 60 | } |
| 61 | extable = (struct exception_table_entry *)mod->extable; |
| 62 | extable[0].insn = (unsigned long)new; |
| 63 | } |
| 64 | |
| 65 | |
| 66 | /* |
| 67 | * exception tables get their symbols translated so we need |
| 68 | * to use a fake function to put in there, which we can then |
| 69 | * replace at runtime. |
| 70 | */ |
| 71 | void foo_label(void); |
| 72 | |
| 73 | /* |
| 74 | * returns 0 for not-executable, negative for executable |
| 75 | * |
| 76 | * Note: we cannot allow this function to be inlined, because |
| 77 | * that would give us more than 1 exception table entry. |
| 78 | * This in turn would break the assumptions above. |
| 79 | */ |
| 80 | static noinline int test_address(void *address) |
| 81 | { |
| 82 | unsigned long result; |
| 83 | |
| 84 | /* Set up an exception table entry for our address */ |
| 85 | fudze_exception_table(&foo_label, address); |
| 86 | result = 1; |
| 87 | asm volatile( |
| 88 | "foo_label:\n" |
| 89 | "0: call *%[fake_code]\n" |
| 90 | "1:\n" |
| 91 | ".section .fixup,\"ax\"\n" |
| 92 | "2: mov %[zero], %[rslt]\n" |
| 93 | " ret\n" |
| 94 | ".previous\n" |
H. Peter Anvin | 9290909 | 2008-02-04 16:47:56 +0100 | [diff] [blame] | 95 | _ASM_EXTABLE(0b,2b) |
Arjan van de Ven | edeed30 | 2008-01-30 13:34:08 +0100 | [diff] [blame] | 96 | : [rslt] "=r" (result) |
| 97 | : [fake_code] "r" (address), [zero] "r" (0UL), "0" (result) |
| 98 | ); |
| 99 | /* change the exception table back for the next round */ |
| 100 | fudze_exception_table(address, &foo_label); |
| 101 | |
| 102 | if (result) |
| 103 | return -ENODEV; |
| 104 | return 0; |
| 105 | } |
| 106 | |
| 107 | static unsigned char test_data = 0xC3; /* 0xC3 is the opcode for "ret" */ |
| 108 | |
| 109 | static int test_NX(void) |
| 110 | { |
| 111 | int ret = 0; |
| 112 | /* 0xC3 is the opcode for "ret" */ |
| 113 | char stackcode[] = {0xC3, 0x90, 0 }; |
| 114 | char *heap; |
| 115 | |
| 116 | test_data = 0xC3; |
| 117 | |
| 118 | printk(KERN_INFO "Testing NX protection\n"); |
| 119 | |
| 120 | /* Test 1: check if the stack is not executable */ |
| 121 | if (test_address(&stackcode)) { |
| 122 | printk(KERN_ERR "test_nx: stack was executable\n"); |
| 123 | ret = -ENODEV; |
| 124 | } |
| 125 | |
| 126 | |
| 127 | /* Test 2: Check if the heap is executable */ |
| 128 | heap = kmalloc(64, GFP_KERNEL); |
| 129 | if (!heap) |
| 130 | return -ENOMEM; |
| 131 | heap[0] = 0xC3; /* opcode for "ret" */ |
| 132 | |
| 133 | if (test_address(heap)) { |
| 134 | printk(KERN_ERR "test_nx: heap was executable\n"); |
| 135 | ret = -ENODEV; |
| 136 | } |
| 137 | kfree(heap); |
| 138 | |
| 139 | /* |
| 140 | * The following 2 tests currently fail, this needs to get fixed |
| 141 | * Until then, don't run them to avoid too many people getting scared |
| 142 | * by the error message |
| 143 | */ |
Arjan van de Ven | edeed30 | 2008-01-30 13:34:08 +0100 | [diff] [blame] | 144 | |
| 145 | #ifdef CONFIG_DEBUG_RODATA |
| 146 | /* Test 3: Check if the .rodata section is executable */ |
| 147 | if (rodata_test_data != 0xC3) { |
| 148 | printk(KERN_ERR "test_nx: .rodata marker has invalid value\n"); |
| 149 | ret = -ENODEV; |
| 150 | } else if (test_address(&rodata_test_data)) { |
| 151 | printk(KERN_ERR "test_nx: .rodata section is executable\n"); |
| 152 | ret = -ENODEV; |
| 153 | } |
| 154 | #endif |
| 155 | |
Arjan van de Ven | 984bb80 | 2008-02-06 22:39:45 +0100 | [diff] [blame] | 156 | #if 0 |
Arjan van de Ven | edeed30 | 2008-01-30 13:34:08 +0100 | [diff] [blame] | 157 | /* Test 4: Check if the .data section of a module is executable */ |
| 158 | if (test_address(&test_data)) { |
| 159 | printk(KERN_ERR "test_nx: .data section is executable\n"); |
| 160 | ret = -ENODEV; |
| 161 | } |
| 162 | |
| 163 | #endif |
| 164 | return 0; |
| 165 | } |
| 166 | |
| 167 | static void test_exit(void) |
| 168 | { |
| 169 | } |
| 170 | |
| 171 | module_init(test_NX); |
| 172 | module_exit(test_exit); |
| 173 | MODULE_LICENSE("GPL"); |
| 174 | MODULE_DESCRIPTION("Testcase for the NX infrastructure"); |
| 175 | MODULE_AUTHOR("Arjan van de Ven <arjan@linux.intel.com>"); |