0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include "lkdtm.h"
0012 #include <linux/stackleak.h>
0013
0014 #if defined(CONFIG_GCC_PLUGIN_STACKLEAK)
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028 static void noinstr check_stackleak_irqoff(void)
0029 {
0030 const unsigned long task_stack_base = (unsigned long)task_stack_page(current);
0031 const unsigned long task_stack_low = stackleak_task_low_bound(current);
0032 const unsigned long task_stack_high = stackleak_task_high_bound(current);
0033 const unsigned long current_sp = current_stack_pointer;
0034 const unsigned long lowest_sp = current->lowest_stack;
0035 unsigned long untracked_high;
0036 unsigned long poison_high, poison_low;
0037 bool test_failed = false;
0038
0039
0040
0041
0042
0043
0044
0045 if (current_sp < task_stack_low || current_sp >= task_stack_high) {
0046 pr_err("FAIL: current_stack_pointer (0x%lx) outside of task stack bounds [0x%lx..0x%lx]\n",
0047 current_sp, task_stack_low, task_stack_high - 1);
0048 test_failed = true;
0049 goto out;
0050 }
0051 if (lowest_sp < task_stack_low || lowest_sp >= task_stack_high) {
0052 pr_err("FAIL: current->lowest_stack (0x%lx) outside of task stack bounds [0x%lx..0x%lx]\n",
0053 lowest_sp, task_stack_low, task_stack_high - 1);
0054 test_failed = true;
0055 goto out;
0056 }
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069 untracked_high = min(current_sp, lowest_sp);
0070 untracked_high = ALIGN_DOWN(untracked_high, sizeof(unsigned long));
0071
0072
0073
0074
0075 poison_high = stackleak_find_top_of_poison(task_stack_low, untracked_high);
0076
0077
0078
0079
0080
0081
0082 poison_low = poison_high;
0083 while (poison_low > task_stack_low) {
0084 poison_low -= sizeof(unsigned long);
0085
0086 if (*(unsigned long *)poison_low == STACKLEAK_POISON)
0087 continue;
0088
0089 pr_err("FAIL: non-poison value %lu bytes below poison boundary: 0x%lx\n",
0090 poison_high - poison_low, *(unsigned long *)poison_low);
0091 test_failed = true;
0092 }
0093
0094 pr_info("stackleak stack usage:\n"
0095 " high offset: %lu bytes\n"
0096 " current: %lu bytes\n"
0097 " lowest: %lu bytes\n"
0098 " tracked: %lu bytes\n"
0099 " untracked: %lu bytes\n"
0100 " poisoned: %lu bytes\n"
0101 " low offset: %lu bytes\n",
0102 task_stack_base + THREAD_SIZE - task_stack_high,
0103 task_stack_high - current_sp,
0104 task_stack_high - lowest_sp,
0105 task_stack_high - untracked_high,
0106 untracked_high - poison_high,
0107 poison_high - task_stack_low,
0108 task_stack_low - task_stack_base);
0109
0110 out:
0111 if (test_failed) {
0112 pr_err("FAIL: the thread stack is NOT properly erased!\n");
0113 } else {
0114 pr_info("OK: the rest of the thread stack is properly erased\n");
0115 }
0116 }
0117
0118 static void lkdtm_STACKLEAK_ERASING(void)
0119 {
0120 unsigned long flags;
0121
0122 local_irq_save(flags);
0123 check_stackleak_irqoff();
0124 local_irq_restore(flags);
0125 }
0126 #else
0127 static void lkdtm_STACKLEAK_ERASING(void)
0128 {
0129 if (IS_ENABLED(CONFIG_HAVE_ARCH_STACKLEAK)) {
0130 pr_err("XFAIL: stackleak is not enabled (CONFIG_GCC_PLUGIN_STACKLEAK=n)\n");
0131 } else {
0132 pr_err("XFAIL: stackleak is not supported on this arch (HAVE_ARCH_STACKLEAK=n)\n");
0133 }
0134 }
0135 #endif
0136
0137 static struct crashtype crashtypes[] = {
0138 CRASHTYPE(STACKLEAK_ERASING),
0139 };
0140
0141 struct crashtype_category stackleak_crashtypes = {
0142 .crashtypes = crashtypes,
0143 .len = ARRAY_SIZE(crashtypes),
0144 };