diff mbox series

[1/4] lkdtm: Avoid more compiler optimizations for bad writes

Message ID 20200529200347.2464284-2-keescook@chromium.org (mailing list archive)
State New
Headers show
Series lkdtm: Various clean ups | expand

Commit Message

Kees Cook May 29, 2020, 8:03 p.m. UTC
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>
Signed-off-by: Kees Cook <keescook@chromium.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(-)

Comments

Nick Desaulniers May 29, 2020, 8:10 p.m. UTC | #1
On Fri, May 29, 2020 at 1:03 PM Kees Cook <keescook@chromium.org> wrote:
>
> 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

Heh, yep.  I recall the exact patch in LLVM causing build breakages
for kernels and various parts of Android userspace within the past
year, for code that tried to write to variables declared const through
casts that removed the const. (Was the last patch for us to build MIPS
IIRC).  Doing so is explicitly UB.  I did feel that that particular
"optimization" was very specific to C/C++, and should not have been
performed in LLVM (which should be more agnostic to the front end
language's wacky rules, IMO) but rather Clang (which doesn't do much
C/C++ language specific optimizations currently, though there are
rough plans forming to change that).

> 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>
> Signed-off-by: Kees Cook <keescook@chromium.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 886459e0ddd9..e1b43f615549 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 62f76d506f04..2dede2ef658f 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 e172719dd86d..b833367a45d0 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
>
> --
> You received this message because you are subscribed to the Google Groups "Clang Built Linux" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to clang-built-linux+unsubscribe@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/clang-built-linux/20200529200347.2464284-2-keescook%40chromium.org.
diff mbox series

Patch

diff --git a/drivers/misc/lkdtm/bugs.c b/drivers/misc/lkdtm/bugs.c
index 886459e0ddd9..e1b43f615549 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 62f76d506f04..2dede2ef658f 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 e172719dd86d..b833367a45d0 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);