From bc0b8cc6cb26a209fa1679d5c063b47bc0afe964 Mon Sep 17 00:00:00 2001 From: Laura Abbott Date: Thu, 25 Feb 2016 16:36:42 -0800 Subject: lkdtm: Add READ_AFTER_FREE test In a similar manner to WRITE_AFTER_FREE, add a READ_AFTER_FREE test to test free poisoning features. Sample output when no sanitization is present: # echo READ_AFTER_FREE > /sys/kernel/debug/provoke-crash/DIRECT [ 17.542473] lkdtm: Performing direct entry READ_AFTER_FREE [ 17.543866] lkdtm: Value in memory before free: 12345678 [ 17.545212] lkdtm: Attempting bad read from freed memory [ 17.546542] lkdtm: Memory was not poisoned with slub_debug=P: # echo READ_AFTER_FREE > /sys/kernel/debug/provoke-crash/DIRECT [ 22.415531] lkdtm: Performing direct entry READ_AFTER_FREE [ 22.416366] lkdtm: Value in memory before free: 12345678 [ 22.417137] lkdtm: Attempting bad read from freed memory [ 22.417897] lkdtm: Memory correctly poisoned, calling BUG Signed-off-by: Laura Abbott Signed-off-by: Kees Cook --- drivers/misc/lkdtm.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'drivers/misc') diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c index 11fdadc68e53..8de47462638a 100644 --- a/drivers/misc/lkdtm.c +++ b/drivers/misc/lkdtm.c @@ -92,6 +92,7 @@ enum ctype { CT_UNALIGNED_LOAD_STORE_WRITE, CT_OVERWRITE_ALLOCATION, CT_WRITE_AFTER_FREE, + CT_READ_AFTER_FREE, CT_SOFTLOCKUP, CT_HARDLOCKUP, CT_SPINLOCKUP, @@ -129,6 +130,7 @@ static char* cp_type[] = { "UNALIGNED_LOAD_STORE_WRITE", "OVERWRITE_ALLOCATION", "WRITE_AFTER_FREE", + "READ_AFTER_FREE", "SOFTLOCKUP", "HARDLOCKUP", "SPINLOCKUP", @@ -417,6 +419,42 @@ static void lkdtm_do_action(enum ctype which) memset(data, 0x78, len); break; } + 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); + if (!val) + break; + + *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. */ + pr_info("Memory correctly poisoned, calling BUG\n"); + BUG(); + } + pr_info("Memory was not poisoned\n"); + + kfree(val); + break; + } case CT_SOFTLOCKUP: preempt_disable(); for (;;) -- cgit v1.2.1 From 250a8988ef4071d8b7cdbb27388f09f33402293a Mon Sep 17 00:00:00 2001 From: Laura Abbott Date: Thu, 25 Feb 2016 16:36:43 -0800 Subject: lkdtm: Update WRITE_AFTER_FREE test The SLUB allocator may use the first word of a freed block to store the freelist information. This may make it harder to test poisoning features. Change the WRITE_AFTER_FREE test to better match what the READ_AFTER_FREE test does and also print out a big more information. Signed-off-by: Laura Abbott Signed-off-by: Kees Cook --- drivers/misc/lkdtm.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c index 8de47462638a..a00a2b11b821 100644 --- a/drivers/misc/lkdtm.c +++ b/drivers/misc/lkdtm.c @@ -411,12 +411,21 @@ static void lkdtm_do_action(enum ctype which) break; } case CT_WRITE_AFTER_FREE: { + int *base; size_t len = 1024; - u32 *data = kmalloc(len, GFP_KERNEL); + /* + * 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; - kfree(data); - schedule(); - memset(data, 0x78, len); + base = kmalloc(len, GFP_KERNEL); + pr_info("Allocated memory %p-%p\n", base, &base[offset * 2]); + kfree(base); + pr_info("Attempting bad write to freed memory at %p\n", + &base[offset]); + base[offset] = 0x0abcdef0; break; } case CT_READ_AFTER_FREE: { -- cgit v1.2.1 From 920d451f9ce68e306b1f35b2029450093163d476 Mon Sep 17 00:00:00 2001 From: Laura Abbott Date: Thu, 25 Feb 2016 16:36:44 -0800 Subject: lkdtm: Add read/write after free tests for buddy memory The current tests for read/write after free work on slab allocated memory. Memory straight from the buddy allocator may behave slightly differently and have a different set of parameters to test. Add tests for those cases as well. On a basic x86 boot: # echo WRITE_BUDDY_AFTER_FREE > /sys/kernel/debug/provoke-crash/DIRECT [ 22.291950] lkdtm: Performing direct entry WRITE_BUDDY_AFTER_FREE [ 22.292983] lkdtm: Writing to the buddy page before free [ 22.293950] lkdtm: Attempting bad write to the buddy page after free # echo READ_BUDDY_AFTER_FREE > /sys/kernel/debug/provoke-crash/DIRECT [ 32.375601] lkdtm: Performing direct entry READ_BUDDY_AFTER_FREE [ 32.379896] lkdtm: Value in memory before free: 12345678 [ 32.383854] lkdtm: Attempting to read from freed memory [ 32.389309] lkdtm: Buddy page was not poisoned On x86 with CONFIG_DEBUG_PAGEALLOC and debug_pagealloc=on: # echo WRITE_BUDDY_AFTER_FREE > /sys/kernel/debug/provoke-crash/DIRECT [ 17.475533] lkdtm: Performing direct entry WRITE_BUDDY_AFTER_FREE [ 17.477360] lkdtm: Writing to the buddy page before free [ 17.479089] lkdtm: Attempting bad write to the buddy page after free [ 17.480904] BUG: unable to handle kernel paging request at ffff88000ebd8000 # echo READ_BUDDY_AFTER_FREE > /sys/kernel/debug/provoke-crash/DIRECT [ 14.606433] lkdtm: Performing direct entry READ_BUDDY_AFTER_FREE [ 14.607447] lkdtm: Value in memory before free: 12345678 [ 14.608161] lkdtm: Attempting to read from freed memory [ 14.608860] BUG: unable to handle kernel paging request at ffff88000eba3000 Note that arches without ARCH_SUPPORTS_DEBUG_PAGEALLOC may not produce the same crash. Signed-off-by: Laura Abbott Signed-off-by: Kees Cook --- drivers/misc/lkdtm.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'drivers/misc') diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c index a00a2b11b821..8e00e2e992d1 100644 --- a/drivers/misc/lkdtm.c +++ b/drivers/misc/lkdtm.c @@ -93,6 +93,8 @@ enum ctype { CT_OVERWRITE_ALLOCATION, CT_WRITE_AFTER_FREE, CT_READ_AFTER_FREE, + CT_WRITE_BUDDY_AFTER_FREE, + CT_READ_BUDDY_AFTER_FREE, CT_SOFTLOCKUP, CT_HARDLOCKUP, CT_SPINLOCKUP, @@ -131,6 +133,8 @@ static char* cp_type[] = { "OVERWRITE_ALLOCATION", "WRITE_AFTER_FREE", "READ_AFTER_FREE", + "WRITE_BUDDY_AFTER_FREE", + "READ_BUDDY_AFTER_FREE", "SOFTLOCKUP", "HARDLOCKUP", "SPINLOCKUP", @@ -464,6 +468,47 @@ static void lkdtm_do_action(enum ctype which) kfree(val); break; } + 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); + schedule(); + pr_info("Attempting bad write to the buddy page after free\n"); + memset((void *)p, 0x78, PAGE_SIZE); + break; + } + case CT_READ_BUDDY_AFTER_FREE: { + unsigned long p = __get_free_page(GFP_KERNEL); + int saw, *val = kmalloc(1024, GFP_KERNEL); + int *base; + + if (!p) + break; + + if (!val) + break; + + 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. */ + pr_info("Buddy page correctly poisoned, calling BUG\n"); + BUG(); + } + pr_info("Buddy page was not poisoned\n"); + + kfree(val); + break; + } case CT_SOFTLOCKUP: preempt_disable(); for (;;) -- cgit v1.2.1 From 5fd9e48084f5566aafb759882f549f37e5940501 Mon Sep 17 00:00:00 2001 From: David Windsor Date: Thu, 17 Dec 2015 00:56:36 -0500 Subject: lkdtm: add test for atomic_t underflow/overflow dmesg output of running this LKDTM test with PaX: [187095.475573] lkdtm: No crash points registered, enable through debugfs [187118.020257] lkdtm: Performing direct entry WRAP_ATOMIC [187118.030045] lkdtm: attempting atomic underflow [187118.030929] PAX: refcount overflow detected in: bash:1790, uid/euid: 0/0 [187118.071667] PAX: refcount overflow occured at: lkdtm_do_action+0x19e/0x400 [lkdtm] [187118.081423] CPU: 3 PID: 1790 Comm: bash Not tainted 4.2.6-pax-refcount-split+ #2 [187118.083403] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006 [187118.102596] task: ffff8800da8de040 ti: ffff8800da8e4000 task.ti: ffff8800da8e4000 [187118.111321] RIP: 0010:[] [] lkdtm_do_action+0x19e/0x400 [lkdtm] ... [187118.128074] lkdtm: attempting atomic overflow [187118.128080] PAX: refcount overflow detected in: bash:1790, uid/euid: 0/0 [187118.128082] PAX: refcount overflow occured at: lkdtm_do_action+0x1b6/0x400 [lkdtm] [187118.128085] CPU: 3 PID: 1790 Comm: bash Not tainted 4.2.6-pax-refcount-split+ #2 [187118.128086] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006 [187118.128088] task: ffff8800da8de040 ti: ffff8800da8e4000 task.ti: ffff8800da8e4000 [187118.128092] RIP: 0010:[] [] lkdtm_do_action+0x1b6/0x400 [lkdtm] Signed-off-by: David Windsor [cleaned up whitespacing, keescook] Signed-off-by: Kees Cook --- drivers/misc/lkdtm.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/misc') diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c index 8e00e2e992d1..c333e813ed34 100644 --- a/drivers/misc/lkdtm.c +++ b/drivers/misc/lkdtm.c @@ -107,6 +107,7 @@ enum ctype { CT_ACCESS_USERSPACE, CT_WRITE_RO, CT_WRITE_KERN, + CT_WRAP_ATOMIC }; static char* cp_name[] = { @@ -147,6 +148,7 @@ static char* cp_type[] = { "ACCESS_USERSPACE", "WRITE_RO", "WRITE_KERN", + "WRAP_ATOMIC" }; static struct jprobe lkdtm; @@ -620,6 +622,17 @@ static void lkdtm_do_action(enum ctype which) do_overwritten(); break; } + 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; + } case CT_NONE: default: break; -- cgit v1.2.1 From 7c0ae5be821c1b6a700c5506de9b62e95f60df3c Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 26 Feb 2016 15:27:35 -0800 Subject: lkdtm: improve use-after-free tests This improves the order of operations on the use-after-free tests to try to make sure we've executed any available sanity-checking code, and to report the poisoning that was found. Signed-off-by: Kees Cook --- drivers/misc/lkdtm.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c index c333e813ed34..9345999f5673 100644 --- a/drivers/misc/lkdtm.c +++ b/drivers/misc/lkdtm.c @@ -417,7 +417,7 @@ static void lkdtm_do_action(enum ctype which) break; } case CT_WRITE_AFTER_FREE: { - int *base; + int *base, *again; size_t len = 1024; /* * The slub allocator uses the first word to store the free @@ -428,10 +428,16 @@ static void lkdtm_do_action(enum ctype which) base = kmalloc(len, GFP_KERNEL); pr_info("Allocated memory %p-%p\n", base, &base[offset * 2]); - kfree(base); pr_info("Attempting bad write to freed memory at %p\n", &base[offset]); + kfree(base); base[offset] = 0x0abcdef0; + /* 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"); + break; } case CT_READ_AFTER_FREE: { @@ -462,7 +468,7 @@ static void lkdtm_do_action(enum ctype which) saw = base[offset]; if (saw != *val) { /* Good! Poisoning happened, so declare a win. */ - pr_info("Memory correctly poisoned, calling BUG\n"); + pr_info("Memory correctly poisoned (%x)\n", saw); BUG(); } pr_info("Memory was not poisoned\n"); @@ -480,6 +486,11 @@ static void lkdtm_do_action(enum ctype which) schedule(); pr_info("Attempting bad write to the buddy page after free\n"); memset((void *)p, 0x78, PAGE_SIZE); + /* Attempt to notice the overwrite. */ + p = __get_free_page(GFP_KERNEL); + free_page(p); + schedule(); + break; } case CT_READ_BUDDY_AFTER_FREE: { @@ -503,7 +514,7 @@ static void lkdtm_do_action(enum ctype which) saw = base[0]; if (saw != *val) { /* Good! Poisoning happened, so declare a win. */ - pr_info("Buddy page correctly poisoned, calling BUG\n"); + pr_info("Memory correctly poisoned (%x)\n", saw); BUG(); } pr_info("Buddy page was not poisoned\n"); -- cgit v1.2.1