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

lkdtm/bugs: Check that a per-task stack canary exists

Introduce REPORT_STACK_CANARY to check for differing stack canaries
between two processes (i.e. that an architecture is correctly implementing
per-task stack canaries), using the task_struct canary as the hint to
locate in the stack. Requires that one of the processes being tested
not be pid 1.

Cc: Ard Biesheuvel <ardb@kernel.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Kees Cook <keescook@chromium.org>
Link: https://lore.kernel.org/r/20211022223826.330653-3-keescook@chromium.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Kees Cook and committed by
Greg Kroah-Hartman
d46e58ef 149538cd

+81
+77
drivers/misc/lkdtm/bugs.c
··· 151 151 pr_info("Stack offset: %d\n", (int)(stack_addr - (uintptr_t)&magic)); 152 152 } 153 153 154 + static pid_t stack_canary_pid; 155 + static unsigned long stack_canary; 156 + static unsigned long stack_canary_offset; 157 + 158 + static noinline void __lkdtm_REPORT_STACK_CANARY(void *stack) 159 + { 160 + int i = 0; 161 + pid_t pid = task_pid_nr(current); 162 + unsigned long *canary = (unsigned long *)stack; 163 + unsigned long current_offset = 0, init_offset = 0; 164 + 165 + /* Do our best to find the canary in a 16 word window ... */ 166 + for (i = 1; i < 16; i++) { 167 + canary = (unsigned long *)stack + i; 168 + #ifdef CONFIG_STACKPROTECTOR 169 + if (*canary == current->stack_canary) 170 + current_offset = i; 171 + if (*canary == init_task.stack_canary) 172 + init_offset = i; 173 + #endif 174 + } 175 + 176 + if (current_offset == 0) { 177 + /* 178 + * If the canary doesn't match what's in the task_struct, 179 + * we're either using a global canary or the stack frame 180 + * layout changed. 181 + */ 182 + if (init_offset != 0) { 183 + pr_err("FAIL: global stack canary found at offset %ld (canary for pid %d matches init_task's)!\n", 184 + init_offset, pid); 185 + } else { 186 + pr_warn("FAIL: did not correctly locate stack canary :(\n"); 187 + pr_expected_config(CONFIG_STACKPROTECTOR); 188 + } 189 + 190 + return; 191 + } else if (init_offset != 0) { 192 + pr_warn("WARNING: found both current and init_task canaries nearby?!\n"); 193 + } 194 + 195 + canary = (unsigned long *)stack + current_offset; 196 + if (stack_canary_pid == 0) { 197 + stack_canary = *canary; 198 + stack_canary_pid = pid; 199 + stack_canary_offset = current_offset; 200 + pr_info("Recorded stack canary for pid %d at offset %ld\n", 201 + stack_canary_pid, stack_canary_offset); 202 + } else if (pid == stack_canary_pid) { 203 + pr_warn("ERROR: saw pid %d again -- please use a new pid\n", pid); 204 + } else { 205 + if (current_offset != stack_canary_offset) { 206 + pr_warn("ERROR: canary offset changed from %ld to %ld!?\n", 207 + stack_canary_offset, current_offset); 208 + return; 209 + } 210 + 211 + if (*canary == stack_canary) { 212 + pr_warn("FAIL: canary identical for pid %d and pid %d at offset %ld!\n", 213 + stack_canary_pid, pid, current_offset); 214 + } else { 215 + pr_info("ok: stack canaries differ between pid %d and pid %d at offset %ld.\n", 216 + stack_canary_pid, pid, current_offset); 217 + /* Reset the test. */ 218 + stack_canary_pid = 0; 219 + } 220 + } 221 + } 222 + 223 + void lkdtm_REPORT_STACK_CANARY(void) 224 + { 225 + /* Use default char array length that triggers stack protection. */ 226 + char data[8] __aligned(sizeof(void *)) = { }; 227 + 228 + __lkdtm_REPORT_STACK_CANARY((void *)&data); 229 + } 230 + 154 231 void lkdtm_UNALIGNED_LOAD_STORE_WRITE(void) 155 232 { 156 233 static u8 data[5] __attribute__((aligned(4))) = {1, 2, 3, 4, 5};
+1
drivers/misc/lkdtm/core.c
··· 111 111 CRASHTYPE(CORRUPT_STACK), 112 112 CRASHTYPE(CORRUPT_STACK_STRONG), 113 113 CRASHTYPE(REPORT_STACK), 114 + CRASHTYPE(REPORT_STACK_CANARY), 114 115 CRASHTYPE(CORRUPT_LIST_ADD), 115 116 CRASHTYPE(CORRUPT_LIST_DEL), 116 117 CRASHTYPE(STACK_GUARD_PAGE_LEADING),
+1
drivers/misc/lkdtm/lkdtm.h
··· 69 69 void lkdtm_CORRUPT_STACK(void); 70 70 void lkdtm_CORRUPT_STACK_STRONG(void); 71 71 void lkdtm_REPORT_STACK(void); 72 + void lkdtm_REPORT_STACK_CANARY(void); 72 73 void lkdtm_UNALIGNED_LOAD_STORE_WRITE(void); 73 74 void lkdtm_SOFTLOCKUP(void); 74 75 void lkdtm_HARDLOCKUP(void);
+1
tools/testing/selftests/lkdtm/config
··· 8 8 CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y 9 9 CONFIG_UBSAN_BOUNDS=y 10 10 CONFIG_UBSAN_TRAP=y 11 + CONFIG_STACKPROTECTOR_STRONG=y
+1
tools/testing/selftests/lkdtm/tests.txt
··· 12 12 CORRUPT_LIST_DEL list_del corruption 13 13 STACK_GUARD_PAGE_LEADING 14 14 STACK_GUARD_PAGE_TRAILING 15 + REPORT_STACK_CANARY repeat:2 ok: stack canaries differ 15 16 UNSET_SMEP pinned CR4 bits changed: 16 17 DOUBLE_FAULT 17 18 CORRUPT_PAC