lkdtm/bugs: Check that a per-task stack canary exists
authorKees Cook <keescook@chromium.org>
Fri, 22 Oct 2021 22:38:26 +0000 (15:38 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 25 Oct 2021 07:13:46 +0000 (09:13 +0200)
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>
drivers/misc/lkdtm/bugs.c
drivers/misc/lkdtm/core.c
drivers/misc/lkdtm/lkdtm.h
tools/testing/selftests/lkdtm/config
tools/testing/selftests/lkdtm/tests.txt

index 4282b625200f53e4782d0e7053e5ee8791177283..f4cb94a9aa9cf86d2300ec6d7e6b0d54a14a9025 100644 (file)
@@ -151,6 +151,83 @@ void lkdtm_REPORT_STACK(void)
        pr_info("Stack offset: %d\n", (int)(stack_addr - (uintptr_t)&magic));
 }
 
+static pid_t stack_canary_pid;
+static unsigned long stack_canary;
+static unsigned long stack_canary_offset;
+
+static noinline void __lkdtm_REPORT_STACK_CANARY(void *stack)
+{
+       int i = 0;
+       pid_t pid = task_pid_nr(current);
+       unsigned long *canary = (unsigned long *)stack;
+       unsigned long current_offset = 0, init_offset = 0;
+
+       /* Do our best to find the canary in a 16 word window ... */
+       for (i = 1; i < 16; i++) {
+               canary = (unsigned long *)stack + i;
+#ifdef CONFIG_STACKPROTECTOR
+               if (*canary == current->stack_canary)
+                       current_offset = i;
+               if (*canary == init_task.stack_canary)
+                       init_offset = i;
+#endif
+       }
+
+       if (current_offset == 0) {
+               /*
+                * If the canary doesn't match what's in the task_struct,
+                * we're either using a global canary or the stack frame
+                * layout changed.
+                */
+               if (init_offset != 0) {
+                       pr_err("FAIL: global stack canary found at offset %ld (canary for pid %d matches init_task's)!\n",
+                              init_offset, pid);
+               } else {
+                       pr_warn("FAIL: did not correctly locate stack canary :(\n");
+                       pr_expected_config(CONFIG_STACKPROTECTOR);
+               }
+
+               return;
+       } else if (init_offset != 0) {
+               pr_warn("WARNING: found both current and init_task canaries nearby?!\n");
+       }
+
+       canary = (unsigned long *)stack + current_offset;
+       if (stack_canary_pid == 0) {
+               stack_canary = *canary;
+               stack_canary_pid = pid;
+               stack_canary_offset = current_offset;
+               pr_info("Recorded stack canary for pid %d at offset %ld\n",
+                       stack_canary_pid, stack_canary_offset);
+       } else if (pid == stack_canary_pid) {
+               pr_warn("ERROR: saw pid %d again -- please use a new pid\n", pid);
+       } else {
+               if (current_offset != stack_canary_offset) {
+                       pr_warn("ERROR: canary offset changed from %ld to %ld!?\n",
+                               stack_canary_offset, current_offset);
+                       return;
+               }
+
+               if (*canary == stack_canary) {
+                       pr_warn("FAIL: canary identical for pid %d and pid %d at offset %ld!\n",
+                               stack_canary_pid, pid, current_offset);
+               } else {
+                       pr_info("ok: stack canaries differ between pid %d and pid %d at offset %ld.\n",
+                               stack_canary_pid, pid, current_offset);
+                       /* Reset the test. */
+                       stack_canary_pid = 0;
+               }
+       }
+}
+
+void lkdtm_REPORT_STACK_CANARY(void)
+{
+       /* Use default char array length that triggers stack protection. */
+       char data[8] __aligned(sizeof(void *)) = { };
+
+       __lkdtm_REPORT_STACK_CANARY((void *)&data);
+}
+
 void lkdtm_UNALIGNED_LOAD_STORE_WRITE(void)
 {
        static u8 data[5] __attribute__((aligned(4))) = {1, 2, 3, 4, 5};
index fe6fd34b8caf78e9df995990c767ad24a4e4d034..609d9ee2acc060b135ef67dafc20ca7bee48d163 100644 (file)
@@ -111,6 +111,7 @@ static const struct crashtype crashtypes[] = {
        CRASHTYPE(CORRUPT_STACK),
        CRASHTYPE(CORRUPT_STACK_STRONG),
        CRASHTYPE(REPORT_STACK),
+       CRASHTYPE(REPORT_STACK_CANARY),
        CRASHTYPE(CORRUPT_LIST_ADD),
        CRASHTYPE(CORRUPT_LIST_DEL),
        CRASHTYPE(STACK_GUARD_PAGE_LEADING),
index c212a253edde065dbdff7eba0e4bbe5bdcababb7..d6137c70ebbe63c765cea9027e6c6645ecc1ac0b 100644 (file)
@@ -69,6 +69,7 @@ void lkdtm_EXHAUST_STACK(void);
 void lkdtm_CORRUPT_STACK(void);
 void lkdtm_CORRUPT_STACK_STRONG(void);
 void lkdtm_REPORT_STACK(void);
+void lkdtm_REPORT_STACK_CANARY(void);
 void lkdtm_UNALIGNED_LOAD_STORE_WRITE(void);
 void lkdtm_SOFTLOCKUP(void);
 void lkdtm_HARDLOCKUP(void);
index 38edea25631bc7a37e091c4d975b57a2638739d0..a26a3fa9e92556eedd78a7c8bbdc56d121245b9c 100644 (file)
@@ -8,3 +8,4 @@ CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT=y
 CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y
 CONFIG_UBSAN_BOUNDS=y
 CONFIG_UBSAN_TRAP=y
+CONFIG_STACKPROTECTOR_STRONG=y
index 09f7bfa383ccaf75338b86e741567aa5011f1797..6b36b7f5dcf9658efe49a28a3ffaa55725a10d22 100644 (file)
@@ -12,6 +12,7 @@ CORRUPT_LIST_ADD list_add corruption
 CORRUPT_LIST_DEL list_del corruption
 STACK_GUARD_PAGE_LEADING
 STACK_GUARD_PAGE_TRAILING
+REPORT_STACK_CANARY repeat:2 ok: stack canaries differ
 UNSET_SMEP pinned CR4 bits changed:
 DOUBLE_FAULT
 CORRUPT_PAC