x86/test_nx: update testing module after recent kernel changes
PATCH 1/1 x86/test_nx: update testing module after recent kernel changes
"test_nx" isn't uptodate, kernel has been changed since its last revision. For now it only able to crash kernel on loading.
exception tables are relative since commit v3.4-rc3-33-g7062765 ("x86, extable: Switch to relative exception table entries")
Exception entries now unusable for this test, because exception table cannot lays farther than 2gb from covering address. This patch adds DIE_PAGE_FAULT notifier into no_context() function in arch/x86/mm/fault.c and handles these exceptions manually.
modules' data sections now can be ro/nx protected since v2.6.37-rc2-3-g84e1c6b ("x86: Add RO/NX protection for loadable kernel modules")
thus fourth testcase can be enabled.
---
sample oops on module loading before this patch:
[ 2.343411] Testing NX protection [ 2.343457] kernel tried to execute NX-protected page - exploit attempt? (uid: 0) [ 2.343494] BUG: unable to handle kernel paging request at ffff88007c951da5 [ 2.343590] IP: [<ffff88007c951da5>] 0xffff88007c951da4 [ 2.343657] PGD 1e0c063 PUD 1fffc067 PMD 800000007c8001e3 [ 2.343780] Oops: 0011 [#1] SMP [ 2.343872] Modules linked in: test_nx(+) ide_generic eni suni atm mce_inject edac_core ar7part mtd cmd640 sctp cs5520 dccp_ipv6 dccp_ipv4 dccp cs55 35_mfgpt bnep rfcomm bluetooth fuse nfsd exportfs powernow_k8 kvm_amd k8temp i2c_nforce2 evbug pcspkr kvm btrfs zlib_deflate libcrc32c ide_pci_generic ide_core ata_generic pata_acpi sata_nv [last unloaded: cdc_wdm] [ 2.344363] CPU 1 [ 2.344363] Pid: 15140, comm: modprobe Tainted: P 3.7.0-rc8-next-20121211+ #597 Gigabyte Technology Co., Ltd. M52S-S3P/M52S-S3P [ 2.344363] RIP: 0010:[<ffff88007c951da5>] [<ffff88007c951da5>] 0xffff88007c951da4 [ 2.344363] RSP: 0018:ffff88007c951d70 EFLAGS: 00010246 [ 2.344363] RAX: 0000000000000000 RBX: 0000000000000001 RCX: ffff88007fc8f328 [ 2.344363] RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff88007c951da5 [ 2.344363] RBP: ffff88007c951d88 R08: 0000000000000000 R09: 0000000000000000 [ 2.344363] R10: ffff88007c951fd8 R11: 0000000000000992 R12: ffff88007c951da5 [ 2.344363] R13: 0000000000000000 R14: ffff88007b0553e0 R15: ffff88007c951ee8 [ 2.344363] FS: 00007f12352de700(0000) GS:ffff88007fc80000(0000) knlGS:0000000000000000 [ 2.344363] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 2.344363] CR2: ffff88007c951da5 CR3: 000000007a23b000 CR4: 00000000000007e0 [ 2.344363] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 2.344363] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 2.344363] Process modprobe (pid: 15140, threadinfo ffff88007c950000, task ffff880079038000) [ 2.344363] Stack: [ 2.344363] ffffffffa0bf002f ffffffffa0bf0320 ffffffffa0bf00a0 ffff88007c951db8 [ 2.344363] ffffffffa0bf00d5 ffff88007b0553e0 0090c30000000000 ffffffffa0bf0320 [ 2.344363] ffffffffa0bf00a0 ffff88007c951de8 ffffffff810002da ffffffffa0bf0320 [ 2.344363] Call Trace: [ 2.344363] [<ffffffffa0bf002f>] ? foo_label+0x3/0x74 [test_nx] [ 2.344363] [<ffffffffa0bf00a0>] ? foo_label+0x74/0x74 [test_nx] [ 2.344363] [<ffffffffa0bf00d5>] test_NX+0x35/0x120 [test_nx] [ 2.344363] [<ffffffffa0bf00a0>] ? foo_label+0x74/0x74 [test_nx] [ 2.344363] [<ffffffff810002da>] do_one_initcall+0x11a/0x170 [ 2.344363] [<ffffffff810a28ad>] load_module+0x196d/0x1e40 [ 2.344363] [<ffffffff8109e8f0>] ? __unlink_module+0x30/0x30 [ 2.344363] [<ffffffff810a2e46>] sys_init_module+0xc6/0xf0 [ 2.344363] [<ffffffff81801a12>] system_call_fastpath+0x16/0x1b [ 2.344363] Code: bf a0 ff ff ff ff a0 00 bf a0 ff ff ff ff b8 1d 95 7c 00 88 ff ff d5 00 bf a0 ff ff ff ff e0 53 05 7b 00 88 ff ff 00 00 00 00 00 <c3> 90 00 20 03 bf a0 ff ff ff ff a0 00 bf a0 ff ff ff ff e8 1d [ 2.344363] RIP [<ffff88007c951da5>] 0xffff88007c951da4 [ 2.344363] RSP <ffff88007c951d70> [ 2.344363] CR2: ffff88007c951da5 --- arch/x86/kernel/test_nx.c | 82 +++++++++++++++++++-------------------------- arch/x86/mm/fault.c | 4 ++ 2 files changed, 39 insertions(+), 47 deletions(-)
arch/x86/kernel/test_nx.c +35 -47
@@ -12,6 +12,7 @@#include <linux/module.h> #include <linux/sort.h> #include <linux/slab.h> +#include <linux/kdebug.h> #include <asm/uaccess.h> #include <asm/asm.h>
@@ -27,48 +28,30 @@ extern int rodata_test_data;* * 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) */ +static void *exception_label; +void fixup_label (void ); -/* -* 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) +static int die_notify (struct notifier_block *nb,unsigned long val,void *data) { -struct module *mod =THIS_MODULE ; -struct exception_table_entry *extable; +struct die_args *args = data; +struct pt_regs *regs = args->regs; -/* -* 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 ; +if (val ==DIE_PAGE_FAULT && + regs->ip == (unsigned long )exception_label) { + regs->ip = (unsigned long )fixup_label; +return NOTIFY_STOP ; } - extable = (struct exception_table_entry *)mod->extable; - extable[0 ].insn = (unsigned long )new; -} +return NOTIFY_DONE ; +} -/* -* 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 ); +struct notifier_block die_notify_block = { + .notifier_call = die_notify, + .priority =INT_MAX ,/* Highest priority */ +};/* * returns 0 for not-executable, negative for executable
@@ -81,23 +64,20 @@ 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); +/* Set up an exception label for our address */ + exception_label = address; + result =1 ; asmvolatile ( -"foo_label:\n" -"0: call *%[fake_code]\n" -"1:\n" +" call *%[fake_code]\n" ".section .fixup,\"ax\"\n" -"2: mov %[zero], %[rslt]\n" +"fixup_label:\n" +" mov %[zero], %[rslt]\n" " ret\n" ".previous\n" - _ASM_EXTABLE(0b,2b) : [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 ;
@@ -108,10 +88,14 @@ 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; +int ret; + + ret =register_die_notifier (&die_notify_block); +if (ret) +return ret; test_data =0xC3 ;
@@ -126,8 +110,10 @@ static int test_NX(void)/* Test 2: Check if the heap is executable */ heap =kmalloc (64 ,GFP_KERNEL ); -if (!heap) -return -ENOMEM ; +if (!heap) { + ret = -ENOMEM ; + goto out; + } heap[0 ] =0xC3 ;/* opcode for "ret" */ if (test_address (heap)) {
@@ -153,14 +139,16 @@ static int test_NX(void) }#endif -#if 0 +#ifdef CONFIG_DEBUG_SET_MODULE_RONX /* 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 + +out: +unregister_die_notifier (&die_notify_block);return ret; }
arch/x86/mm/fault.c +4 -0
@@ -664,6 +664,10 @@ no_context(struct pt_regs *regs, unsigned long error_code,if (is_errata93 (regs, address))return ; +if (notify_die (DIE_PAGE_FAULT ,"page fault" , regs, error_code, +X86_TRAP_PF , signal) ==NOTIFY_STOP ) +return ; +/* * Oops. The kernel tried to access some bad page. We'll have to * terminate things with extreme prejudice: