| From 7760d7fb6c3b10122f4c87e37f4fb0718bc1e0dd Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 25 Jun 2020 13:37:01 -0700 |
| Subject: lkdtm: Avoid more compiler optimizations for bad writes |
| |
| From: Kees Cook <keescook@chromium.org> |
| |
| [ Upstream commit 464e86b4abadfc490f426954b431e2ec6a9d7bd2 ] |
| |
| It seems at least Clang is able to throw away writes it knows are |
| destined for read-only memory, which makes things like the WRITE_RO test |
| fail, as the write gets elided. Instead, force the variable to be |
| volatile, and make similar changes through-out other tests in an effort |
| to avoid needing to repeat fixing these kinds of problems. Also includes |
| pr_err() calls in failure paths so that kernel logs are more clear in |
| the failure case. |
| |
| Reported-by: Prasad Sodagudi <psodagud@codeaurora.org> |
| Suggested-by: Sami Tolvanen <samitolvanen@google.com> |
| Fixes: 9ae113ce5faf ("lkdtm: add tests for additional page permissions") |
| Signed-off-by: Kees Cook <keescook@chromium.org> |
| Link: https://lore.kernel.org/r/20200625203704.317097-2-keescook@chromium.org |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/misc/lkdtm/bugs.c | 11 +++++------ |
| drivers/misc/lkdtm/perms.c | 22 +++++++++++++++------- |
| drivers/misc/lkdtm/usercopy.c | 7 +++++-- |
| 3 files changed, 25 insertions(+), 15 deletions(-) |
| |
| diff --git a/drivers/misc/lkdtm/bugs.c b/drivers/misc/lkdtm/bugs.c |
| index 886459e0ddd98..e1b43f6155496 100644 |
| --- a/drivers/misc/lkdtm/bugs.c |
| +++ b/drivers/misc/lkdtm/bugs.c |
| @@ -118,9 +118,8 @@ noinline void lkdtm_CORRUPT_STACK(void) |
| /* Use default char array length that triggers stack protection. */ |
| char data[8] __aligned(sizeof(void *)); |
| |
| - __lkdtm_CORRUPT_STACK(&data); |
| - |
| - pr_info("Corrupted stack containing char array ...\n"); |
| + pr_info("Corrupting stack containing char array ...\n"); |
| + __lkdtm_CORRUPT_STACK((void *)&data); |
| } |
| |
| /* Same as above but will only get a canary with -fstack-protector-strong */ |
| @@ -131,9 +130,8 @@ noinline void lkdtm_CORRUPT_STACK_STRONG(void) |
| unsigned long *ptr; |
| } data __aligned(sizeof(void *)); |
| |
| - __lkdtm_CORRUPT_STACK(&data); |
| - |
| - pr_info("Corrupted stack containing union ...\n"); |
| + pr_info("Corrupting stack containing union ...\n"); |
| + __lkdtm_CORRUPT_STACK((void *)&data); |
| } |
| |
| void lkdtm_UNALIGNED_LOAD_STORE_WRITE(void) |
| @@ -248,6 +246,7 @@ void lkdtm_ARRAY_BOUNDS(void) |
| |
| kfree(not_checked); |
| kfree(checked); |
| + pr_err("FAIL: survived array bounds overflow!\n"); |
| } |
| |
| void lkdtm_CORRUPT_LIST_ADD(void) |
| diff --git a/drivers/misc/lkdtm/perms.c b/drivers/misc/lkdtm/perms.c |
| index 62f76d506f040..2dede2ef658f3 100644 |
| --- a/drivers/misc/lkdtm/perms.c |
| +++ b/drivers/misc/lkdtm/perms.c |
| @@ -57,6 +57,7 @@ static noinline void execute_location(void *dst, bool write) |
| } |
| pr_info("attempting bad execution at %px\n", func); |
| func(); |
| + pr_err("FAIL: func returned\n"); |
| } |
| |
| static void execute_user_location(void *dst) |
| @@ -75,20 +76,22 @@ static void execute_user_location(void *dst) |
| return; |
| pr_info("attempting bad execution at %px\n", func); |
| func(); |
| + pr_err("FAIL: func returned\n"); |
| } |
| |
| void lkdtm_WRITE_RO(void) |
| { |
| - /* Explicitly cast away "const" for the test. */ |
| - unsigned long *ptr = (unsigned long *)&rodata; |
| + /* Explicitly cast away "const" for the test and make volatile. */ |
| + volatile unsigned long *ptr = (unsigned long *)&rodata; |
| |
| pr_info("attempting bad rodata write at %px\n", ptr); |
| *ptr ^= 0xabcd1234; |
| + pr_err("FAIL: survived bad write\n"); |
| } |
| |
| void lkdtm_WRITE_RO_AFTER_INIT(void) |
| { |
| - unsigned long *ptr = &ro_after_init; |
| + volatile unsigned long *ptr = &ro_after_init; |
| |
| /* |
| * Verify we were written to during init. Since an Oops |
| @@ -102,19 +105,21 @@ void lkdtm_WRITE_RO_AFTER_INIT(void) |
| |
| pr_info("attempting bad ro_after_init write at %px\n", ptr); |
| *ptr ^= 0xabcd1234; |
| + pr_err("FAIL: survived bad write\n"); |
| } |
| |
| void lkdtm_WRITE_KERN(void) |
| { |
| size_t size; |
| - unsigned char *ptr; |
| + volatile 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 %px\n", size, ptr); |
| - memcpy(ptr, (unsigned char *)do_nothing, size); |
| + memcpy((void *)ptr, (unsigned char *)do_nothing, size); |
| flush_icache_range((unsigned long)ptr, (unsigned long)(ptr + size)); |
| + pr_err("FAIL: survived bad write\n"); |
| |
| do_overwritten(); |
| } |
| @@ -193,9 +198,11 @@ void lkdtm_ACCESS_USERSPACE(void) |
| pr_info("attempting bad read at %px\n", ptr); |
| tmp = *ptr; |
| tmp += 0xc0dec0de; |
| + pr_err("FAIL: survived bad read\n"); |
| |
| pr_info("attempting bad write at %px\n", ptr); |
| *ptr = tmp; |
| + pr_err("FAIL: survived bad write\n"); |
| |
| vm_munmap(user_addr, PAGE_SIZE); |
| } |
| @@ -203,19 +210,20 @@ void lkdtm_ACCESS_USERSPACE(void) |
| void lkdtm_ACCESS_NULL(void) |
| { |
| unsigned long tmp; |
| - unsigned long *ptr = (unsigned long *)NULL; |
| + volatile unsigned long *ptr = (unsigned long *)NULL; |
| |
| pr_info("attempting bad read at %px\n", ptr); |
| tmp = *ptr; |
| tmp += 0xc0dec0de; |
| + pr_err("FAIL: survived bad read\n"); |
| |
| pr_info("attempting bad write at %px\n", ptr); |
| *ptr = tmp; |
| + pr_err("FAIL: survived bad write\n"); |
| } |
| |
| void __init lkdtm_perms_init(void) |
| { |
| /* Make sure we can write to __ro_after_init values during __init */ |
| ro_after_init |= 0xAA; |
| - |
| } |
| diff --git a/drivers/misc/lkdtm/usercopy.c b/drivers/misc/lkdtm/usercopy.c |
| index e172719dd86d0..b833367a45d05 100644 |
| --- a/drivers/misc/lkdtm/usercopy.c |
| +++ b/drivers/misc/lkdtm/usercopy.c |
| @@ -304,19 +304,22 @@ void lkdtm_USERCOPY_KERNEL(void) |
| return; |
| } |
| |
| - pr_info("attempting good copy_to_user from kernel rodata\n"); |
| + pr_info("attempting good copy_to_user from kernel rodata: %px\n", |
| + test_text); |
| if (copy_to_user((void __user *)user_addr, test_text, |
| unconst + sizeof(test_text))) { |
| pr_warn("copy_to_user failed unexpectedly?!\n"); |
| goto free_user; |
| } |
| |
| - pr_info("attempting bad copy_to_user from kernel text\n"); |
| + pr_info("attempting bad copy_to_user from kernel text: %px\n", |
| + vm_mmap); |
| if (copy_to_user((void __user *)user_addr, vm_mmap, |
| unconst + PAGE_SIZE)) { |
| pr_warn("copy_to_user failed, but lacked Oops\n"); |
| goto free_user; |
| } |
| + pr_err("FAIL: survived bad copy_to_user()\n"); |
| |
| free_user: |
| vm_munmap(user_addr, PAGE_SIZE); |
| -- |
| 2.25.1 |
| |