From 0d9eb29b13f0e326c4e19b85d3a4ac46e335e6d2 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Sun, 26 Jun 2016 15:12:31 -0700 Subject: lkdtm: split memory permissions tests to separate file This splits the EXEC_*, WRITE_* and related tests into the new lkdtm_perms.c file to help separate things better for readability. Signed-off-by: Kees Cook --- drivers/misc/lkdtm_perms.c | 203 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 drivers/misc/lkdtm_perms.c (limited to 'drivers/misc/lkdtm_perms.c') diff --git a/drivers/misc/lkdtm_perms.c b/drivers/misc/lkdtm_perms.c new file mode 100644 index 000000000000..8201006502e2 --- /dev/null +++ b/drivers/misc/lkdtm_perms.c @@ -0,0 +1,203 @@ +/* + * This is for all the tests related to validating kernel memory + * permissions: non-executable regions, non-writable regions, and + * even non-readable regions. + */ +#define pr_fmt(fmt) "lkdtm: " fmt + +#include +#include +#include +#include +#include +#include + +#include "lkdtm.h" + +/* Whether or not to fill the target memory area with do_nothing(). */ +#define CODE_WRITE true +#define CODE_AS_IS false + +/* How many bytes to copy to be sure we've copied enough of do_nothing(). */ +#define EXEC_SIZE 64 + +/* This is non-const, so it will end up in the .data section. */ +static u8 data_area[EXEC_SIZE]; + +/* This is cost, so it will end up in the .rodata section. */ +static const unsigned long rodata = 0xAA55AA55; + +/* This is marked __ro_after_init, so it should ultimately be .rodata. */ +static unsigned long ro_after_init __ro_after_init = 0x55AA5500; + +/* + * This just returns to the caller. It is designed to be copied into + * non-executable memory regions. + */ +static void do_nothing(void) +{ + return; +} + +/* 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; +} + +static noinline void execute_location(void *dst, bool write) +{ + void (*func)(void) = dst; + + pr_info("attempting ok execution at %p\n", do_nothing); + do_nothing(); + + if (write == CODE_WRITE) { + memcpy(dst, do_nothing, EXEC_SIZE); + flush_icache_range((unsigned long)dst, + (unsigned long)dst + EXEC_SIZE); + } + pr_info("attempting bad execution at %p\n", func); + func(); +} + +static void execute_user_location(void *dst) +{ + /* Intentionally crossing kernel/user memory boundary. */ + void (*func)(void) = dst; + + pr_info("attempting ok execution at %p\n", do_nothing); + do_nothing(); + + if (copy_to_user((void __user *)dst, do_nothing, EXEC_SIZE)) + return; + flush_icache_range((unsigned long)dst, (unsigned long)dst + EXEC_SIZE); + pr_info("attempting bad execution at %p\n", func); + func(); +} + +void lkdtm_WRITE_RO(void) +{ + /* Explicitly cast away "const" for the test. */ + unsigned long *ptr = (unsigned long *)&rodata; + + pr_info("attempting bad rodata write at %p\n", ptr); + *ptr ^= 0xabcd1234; +} + +void lkdtm_WRITE_RO_AFTER_INIT(void) +{ + 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); + return; + } + + pr_info("attempting bad ro_after_init write at %p\n", ptr); + *ptr ^= 0xabcd1234; +} + +void lkdtm_WRITE_KERN(void) +{ + 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(); +} + +void lkdtm_EXEC_DATA(void) +{ + execute_location(data_area, CODE_WRITE); +} + +void lkdtm_EXEC_STACK(void) +{ + u8 stack_area[EXEC_SIZE]; + execute_location(stack_area, CODE_WRITE); +} + +void lkdtm_EXEC_KMALLOC(void) +{ + u32 *kmalloc_area = kmalloc(EXEC_SIZE, GFP_KERNEL); + execute_location(kmalloc_area, CODE_WRITE); + kfree(kmalloc_area); +} + +void lkdtm_EXEC_VMALLOC(void) +{ + u32 *vmalloc_area = vmalloc(EXEC_SIZE); + execute_location(vmalloc_area, CODE_WRITE); + vfree(vmalloc_area); +} + +void lkdtm_EXEC_RODATA(void) +{ + execute_location(lkdtm_rodata_do_nothing, CODE_AS_IS); +} + +void lkdtm_EXEC_USERSPACE(void) +{ + 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); +} + +void lkdtm_ACCESS_USERSPACE(void) +{ + unsigned long user_addr, tmp = 0; + 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; + } + + 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; + } + + ptr = (unsigned long *)user_addr; + + pr_info("attempting bad read at %p\n", ptr); + tmp = *ptr; + tmp += 0xc0dec0de; + + pr_info("attempting bad write at %p\n", ptr); + *ptr = tmp; + + vm_munmap(user_addr, PAGE_SIZE); +} + +void __init lkdtm_perms_init(void) +{ + /* Make sure we can write to __ro_after_init values during __init */ + ro_after_init |= 0xAA; + +} -- cgit v1.2.3-70-g09d2 From 6d2e91a662256fd88ec0505567a59d21094ed415 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 15 Jul 2016 16:04:39 -0700 Subject: lkdtm: silence warnings about function declarations When building under W=1, the lack of lkdtm.h in lkdtm_usercopy.c and lkdtm_rodata.c was discovered. This fixes the issue and consolidates the common header and the pr_fmt macro for simplicity and regularity across each test source file. Signed-off-by: Kees Cook --- drivers/misc/lkdtm.h | 5 ++++- drivers/misc/lkdtm_bugs.c | 6 +----- drivers/misc/lkdtm_core.c | 6 +----- drivers/misc/lkdtm_heap.c | 6 +----- drivers/misc/lkdtm_perms.c | 6 +----- drivers/misc/lkdtm_rodata.c | 2 +- drivers/misc/lkdtm_usercopy.c | 4 +--- 7 files changed, 10 insertions(+), 25 deletions(-) (limited to 'drivers/misc/lkdtm_perms.c') diff --git a/drivers/misc/lkdtm.h b/drivers/misc/lkdtm.h index d70a41741bb3..fdf954c2107f 100644 --- a/drivers/misc/lkdtm.h +++ b/drivers/misc/lkdtm.h @@ -1,6 +1,10 @@ #ifndef __LKDTM_H #define __LKDTM_H +#define pr_fmt(fmt) "lkdtm: " fmt + +#include + /* lkdtm_bugs.c */ void __init lkdtm_bugs_init(int *recur_param); void lkdtm_PANIC(void); @@ -53,5 +57,4 @@ void lkdtm_USERCOPY_STACK_FRAME_FROM(void); void lkdtm_USERCOPY_STACK_BEYOND(void); void lkdtm_USERCOPY_KERNEL(void); - #endif diff --git a/drivers/misc/lkdtm_bugs.c b/drivers/misc/lkdtm_bugs.c index e87071f9c003..182ae1894b32 100644 --- a/drivers/misc/lkdtm_bugs.c +++ b/drivers/misc/lkdtm_bugs.c @@ -4,12 +4,8 @@ * lockups) along with other things that don't fit well into existing LKDTM * test source files. */ -#define pr_fmt(fmt) "lkdtm: " fmt - -#include -#include - #include "lkdtm.h" +#include /* * Make sure our attempts to over run the kernel stack doesn't trigger diff --git a/drivers/misc/lkdtm_core.c b/drivers/misc/lkdtm_core.c index 717aad6d374b..f9154b8d67f6 100644 --- a/drivers/misc/lkdtm_core.c +++ b/drivers/misc/lkdtm_core.c @@ -30,9 +30,7 @@ * * See Documentation/fault-injection/provoke-crashes.txt for instructions */ -#define pr_fmt(fmt) "lkdtm: " fmt - -#include +#include "lkdtm.h" #include #include #include @@ -49,8 +47,6 @@ #include #endif -#include "lkdtm.h" - #define DEFAULT_COUNT 10 static int lkdtm_debugfs_open(struct inode *inode, struct file *file); diff --git a/drivers/misc/lkdtm_heap.c b/drivers/misc/lkdtm_heap.c index 12f50e8dcbfe..0f1581664c1c 100644 --- a/drivers/misc/lkdtm_heap.c +++ b/drivers/misc/lkdtm_heap.c @@ -2,12 +2,8 @@ * This is for all the tests relating directly to heap memory, including * page allocation and slab allocations. */ -#define pr_fmt(fmt) "lkdtm: " fmt - -#include -#include - #include "lkdtm.h" +#include /* * This tries to stay within the next largest power-of-2 kmalloc cache diff --git a/drivers/misc/lkdtm_perms.c b/drivers/misc/lkdtm_perms.c index 8201006502e2..45f1c0f96612 100644 --- a/drivers/misc/lkdtm_perms.c +++ b/drivers/misc/lkdtm_perms.c @@ -3,17 +3,13 @@ * permissions: non-executable regions, non-writable regions, and * even non-readable regions. */ -#define pr_fmt(fmt) "lkdtm: " fmt - -#include +#include "lkdtm.h" #include #include #include #include #include -#include "lkdtm.h" - /* Whether or not to fill the target memory area with do_nothing(). */ #define CODE_WRITE true #define CODE_AS_IS false diff --git a/drivers/misc/lkdtm_rodata.c b/drivers/misc/lkdtm_rodata.c index 4d0d851f02b9..166b1db3969f 100644 --- a/drivers/misc/lkdtm_rodata.c +++ b/drivers/misc/lkdtm_rodata.c @@ -2,7 +2,7 @@ * This includes functions that are meant to live entirely in .rodata * (via objcopy tricks), to validate the non-executability of .rodata. */ -#include +#include "lkdtm.h" void lkdtm_rodata_do_nothing(void) { diff --git a/drivers/misc/lkdtm_usercopy.c b/drivers/misc/lkdtm_usercopy.c index 9c748e819a35..5a3fd76eec27 100644 --- a/drivers/misc/lkdtm_usercopy.c +++ b/drivers/misc/lkdtm_usercopy.c @@ -2,9 +2,7 @@ * This is for all the tests related to copy_to_user() and copy_from_user() * hardening. */ -#define pr_fmt(fmt) "lkdtm: " fmt - -#include +#include "lkdtm.h" #include #include #include -- cgit v1.2.3-70-g09d2