[PATCH] sched/kasan: clear stale stack poison

From: Mark Rutland
Date: Tue Mar 01 2016 - 14:27:23 EST


CPUs get hotplugged out some levels deep in C code, and hence when KASAN
is in use, the instrumented function preambles will have left the stack
shadow area poisoned.

This poison is not cleared, so when a CPU re-enters the kernel, it is
possible for accesses in instrumented functions to hit this stale
poison, resulting in (spurious) KASAN splats.

This patch forcefully unpoisons an idle task's stack shadow when it is
re-initialised prior to a hotplug, avoiding spurious hits against stale
poison.

Signed-off-by: Mark Rutland <mark.rutland@xxxxxxx>
---
include/linux/kasan.h | 4 ++++
kernel/sched/core.c | 3 +++
mm/kasan/kasan.c | 10 ++++++++++
3 files changed, 17 insertions(+)

diff --git a/include/linux/kasan.h b/include/linux/kasan.h
index 4b9f85c..e00486f 100644
--- a/include/linux/kasan.h
+++ b/include/linux/kasan.h
@@ -43,6 +43,8 @@ static inline void kasan_disable_current(void)

void kasan_unpoison_shadow(const void *address, size_t size);

+void kasan_unpoison_task_stack(struct task_struct *idle);
+
void kasan_alloc_pages(struct page *page, unsigned int order);
void kasan_free_pages(struct page *page, unsigned int order);

@@ -66,6 +68,8 @@ void kasan_free_shadow(const struct vm_struct *vm);

static inline void kasan_unpoison_shadow(const void *address, size_t size) {}

+static inline void kasan_unpoison_task_stack(struct task_struct *idle) {}
+
static inline void kasan_enable_current(void) {}
static inline void kasan_disable_current(void) {}

diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 9503d59..41f6b22 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -26,6 +26,7 @@
* Thomas Gleixner, Mike Kravetz
*/

+#include <linux/kasan.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/nmi.h>
@@ -5096,6 +5097,8 @@ void init_idle(struct task_struct *idle, int cpu)
idle->state = TASK_RUNNING;
idle->se.exec_start = sched_clock();

+ kasan_unpoison_task_stack(idle);
+
#ifdef CONFIG_SMP
/*
* Its possible that init_idle() gets called multiple times on a task,
diff --git a/mm/kasan/kasan.c b/mm/kasan/kasan.c
index bc0a8d8..467f394 100644
--- a/mm/kasan/kasan.c
+++ b/mm/kasan/kasan.c
@@ -60,6 +60,16 @@ void kasan_unpoison_shadow(const void *address, size_t size)
}
}

+/*
+ * Remove any poison left on the stack from a prior hot-unplug.
+ */
+void kasan_unpoison_task_stack(struct task_struct *idle)
+{
+ void *base = task_stack_page(idle) + sizeof(struct thread_info);
+ size_t size = THREAD_SIZE - sizeof(struct thread_info);
+
+ kasan_unpoison_shadow(base, size);
+}

/*
* All functions below always inlined so compiler could
--
1.9.1