[PATCH v1] kasan, arm64: Unpoison dirty stack frames when resuming from suspend.
Andrey Ryabinin
ryabinin.a.a at gmail.com
Wed Mar 2 04:53:36 PST 2016
On 03/01/2016 10:42 PM, Mark Rutland wrote:
> On Fri, Feb 26, 2016 at 06:28:27PM +0100, Alexander Potapenko wrote:
>> On Fri, Feb 26, 2016 at 2:53 PM, Mark Rutland <mark.rutland at arm.com> wrote:
>>> Hi,
>>>
>>> On Fri, Feb 26, 2016 at 01:38:37PM +0100, Alexander Potapenko wrote:
>>>> Before an ARM64 CPU is suspended, the kernel saves the context which will
>>>> be used to initialize the register state upon resume. After that and
>>>> before the actual execution of the SMC instruction the kernel creates
>>>> several stack frames which are never unpoisoned because arm_smccc_smc()
>>>> does not return. This may cause false positive stack buffer overflow
>>>> reports from KASAN.
>>>>
>>>> The solution is to record the stack pointer value just before the CPU is
>>>> suspended, and unpoison the part of stack between the saved value and
>>>> the stack pointer upon resume.
>>>
>>> Thanks for looking into this! That's much appreciated.
>>>
>>> I think the general approach (unposioning the stack upon cold return to
>>> the kernel) is fine, but I have concerns with the implementation, which
>>> I've noted below.
>
> For the idle case I intend to respin my patch [1] which calls
> kasan_unpoison_shadow from assembly in the resume path, as I think
> that's the only reliable approach.
>
>>> The problem also applies for hotplug, as leftover poison from the
>>> hot-unplug path isn't cleaned before a CPU is hotplugged back on. The
>>> first few functions are likely deterministic in their stack usage, so
>>> it's not seen with a defconfig, but I think it's possible to trigger,
>>> and it's also a cross-architecture problem shared with x86.
>> Agreed, but since I haven't yet seen problems with hotplug, it's hard
>> to test the fix for them.
>
> For the common hotplug case, how about the below?
>
Nah, looks a bit hacky IMO. I think it's better to use cpu hotplug notifier.
I'll send patch shortly.
> I've given it a spin locally on arm64 with the reproducer I posted
> earlier.
>
> Thanks,
> Mark.
>
> [1] http://lists.infradead.org/pipermail/linux-arm-kernel/2016-February/409466.html
>
> ---->8----
> From 34839286826c88338cd91a142b1bcc3c077a87aa Mon Sep 17 00:00:00 2001
> From: Mark Rutland <mark.rutland at arm.com>
> Date: Tue, 1 Mar 2016 19:27:23 +0000
> Subject: [PATCH] sched/kasan: clear stale stack poison
>
> 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 at arm.com>
> ---
> 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
>
More information about the linux-arm-kernel
mailing list