Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

lkdtm: Avoid more compiler optimizations for bad writes

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>

authored by

Kees Cook and committed by
Greg Kroah-Hartman
464e86b4 4afc339e

+25 -15
+5 -6
drivers/misc/lkdtm/bugs.c
··· 118 118 /* Use default char array length that triggers stack protection. */ 119 119 char data[8] __aligned(sizeof(void *)); 120 120 121 - __lkdtm_CORRUPT_STACK(&data); 122 - 123 - pr_info("Corrupted stack containing char array ...\n"); 121 + pr_info("Corrupting stack containing char array ...\n"); 122 + __lkdtm_CORRUPT_STACK((void *)&data); 124 123 } 125 124 126 125 /* Same as above but will only get a canary with -fstack-protector-strong */ ··· 130 131 unsigned long *ptr; 131 132 } data __aligned(sizeof(void *)); 132 133 133 - __lkdtm_CORRUPT_STACK(&data); 134 - 135 - pr_info("Corrupted stack containing union ...\n"); 134 + pr_info("Corrupting stack containing union ...\n"); 135 + __lkdtm_CORRUPT_STACK((void *)&data); 136 136 } 137 137 138 138 void lkdtm_UNALIGNED_LOAD_STORE_WRITE(void) ··· 246 248 247 249 kfree(not_checked); 248 250 kfree(checked); 251 + pr_err("FAIL: survived array bounds overflow!\n"); 249 252 } 250 253 251 254 void lkdtm_CORRUPT_LIST_ADD(void)
+15 -7
drivers/misc/lkdtm/perms.c
··· 57 57 } 58 58 pr_info("attempting bad execution at %px\n", func); 59 59 func(); 60 + pr_err("FAIL: func returned\n"); 60 61 } 61 62 62 63 static void execute_user_location(void *dst) ··· 76 75 return; 77 76 pr_info("attempting bad execution at %px\n", func); 78 77 func(); 78 + pr_err("FAIL: func returned\n"); 79 79 } 80 80 81 81 void lkdtm_WRITE_RO(void) 82 82 { 83 - /* Explicitly cast away "const" for the test. */ 84 - unsigned long *ptr = (unsigned long *)&rodata; 83 + /* Explicitly cast away "const" for the test and make volatile. */ 84 + volatile unsigned long *ptr = (unsigned long *)&rodata; 85 85 86 86 pr_info("attempting bad rodata write at %px\n", ptr); 87 87 *ptr ^= 0xabcd1234; 88 + pr_err("FAIL: survived bad write\n"); 88 89 } 89 90 90 91 void lkdtm_WRITE_RO_AFTER_INIT(void) 91 92 { 92 - unsigned long *ptr = &ro_after_init; 93 + volatile unsigned long *ptr = &ro_after_init; 93 94 94 95 /* 95 96 * Verify we were written to during init. Since an Oops ··· 105 102 106 103 pr_info("attempting bad ro_after_init write at %px\n", ptr); 107 104 *ptr ^= 0xabcd1234; 105 + pr_err("FAIL: survived bad write\n"); 108 106 } 109 107 110 108 void lkdtm_WRITE_KERN(void) 111 109 { 112 110 size_t size; 113 - unsigned char *ptr; 111 + volatile unsigned char *ptr; 114 112 115 113 size = (unsigned long)do_overwritten - (unsigned long)do_nothing; 116 114 ptr = (unsigned char *)do_overwritten; 117 115 118 116 pr_info("attempting bad %zu byte write at %px\n", size, ptr); 119 - memcpy(ptr, (unsigned char *)do_nothing, size); 117 + memcpy((void *)ptr, (unsigned char *)do_nothing, size); 120 118 flush_icache_range((unsigned long)ptr, (unsigned long)(ptr + size)); 119 + pr_err("FAIL: survived bad write\n"); 121 120 122 121 do_overwritten(); 123 122 } ··· 198 193 pr_info("attempting bad read at %px\n", ptr); 199 194 tmp = *ptr; 200 195 tmp += 0xc0dec0de; 196 + pr_err("FAIL: survived bad read\n"); 201 197 202 198 pr_info("attempting bad write at %px\n", ptr); 203 199 *ptr = tmp; 200 + pr_err("FAIL: survived bad write\n"); 204 201 205 202 vm_munmap(user_addr, PAGE_SIZE); 206 203 } ··· 210 203 void lkdtm_ACCESS_NULL(void) 211 204 { 212 205 unsigned long tmp; 213 - unsigned long *ptr = (unsigned long *)NULL; 206 + volatile unsigned long *ptr = (unsigned long *)NULL; 214 207 215 208 pr_info("attempting bad read at %px\n", ptr); 216 209 tmp = *ptr; 217 210 tmp += 0xc0dec0de; 211 + pr_err("FAIL: survived bad read\n"); 218 212 219 213 pr_info("attempting bad write at %px\n", ptr); 220 214 *ptr = tmp; 215 + pr_err("FAIL: survived bad write\n"); 221 216 } 222 217 223 218 void __init lkdtm_perms_init(void) 224 219 { 225 220 /* Make sure we can write to __ro_after_init values during __init */ 226 221 ro_after_init |= 0xAA; 227 - 228 222 }
+5 -2
drivers/misc/lkdtm/usercopy.c
··· 304 304 return; 305 305 } 306 306 307 - pr_info("attempting good copy_to_user from kernel rodata\n"); 307 + pr_info("attempting good copy_to_user from kernel rodata: %px\n", 308 + test_text); 308 309 if (copy_to_user((void __user *)user_addr, test_text, 309 310 unconst + sizeof(test_text))) { 310 311 pr_warn("copy_to_user failed unexpectedly?!\n"); 311 312 goto free_user; 312 313 } 313 314 314 - pr_info("attempting bad copy_to_user from kernel text\n"); 315 + pr_info("attempting bad copy_to_user from kernel text: %px\n", 316 + vm_mmap); 315 317 if (copy_to_user((void __user *)user_addr, vm_mmap, 316 318 unconst + PAGE_SIZE)) { 317 319 pr_warn("copy_to_user failed, but lacked Oops\n"); 318 320 goto free_user; 319 321 } 322 + pr_err("FAIL: survived bad copy_to_user()\n"); 320 323 321 324 free_user: 322 325 vm_munmap(user_addr, PAGE_SIZE);