From: will.deacon@arm.com (Will Deacon)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 14/14] arm64: add VMAP_STACK overflow detection
Date: Mon, 14 Aug 2017 16:32:53 +0100 [thread overview]
Message-ID: <20170814153253.GA22747@arm.com> (raw)
In-Reply-To: <1502130965-18710-15-git-send-email-mark.rutland@arm.com>
Just some minor comments on this (after taking ages to realise you were
using tpidr_el0 as a temporary rather than tpidr_el1 and getting totally
confused!).
On Mon, Aug 07, 2017 at 07:36:05PM +0100, Mark Rutland wrote:
> This patch adds stack overflow detection to arm64, usable when vmap'd stacks
> are in use.
>
> Overflow is detected in a small preamble executed for each exception entry,
> which checks whether there is enough space on the current stack for the general
> purpose registers to be saved. If there is not enough space, the overflow
> handler is invoked on a per-cpu overflow stack. This approach preserves the
> original exception information in ESR_EL1 (and where appropriate, FAR_EL1).
>
> Task and IRQ stacks are aligned to double their size, enabling overflow to be
> detected with a single bit test. For example, a 16K stack is aligned to 32K,
> ensuring that bit 14 of the SP must be zero. On an overflow (or underflow),
> this bit is flipped. Thus, overflow (of less than the size of the stack) can be
> detected by testing whether this bit is set.
>
> The overflow check is performed before any attempt is made to access the
> stack, avoiding recursive faults (and the loss of exception information
> these would entail). As logical operations cannot be performed on the SP
> directly, the SP is temporarily swapped with a general purpose register
> using arithmetic operations to enable the test to be performed.
[...]
> diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h
> index c5cd2c5..1a025b7 100644
> --- a/arch/arm64/include/asm/memory.h
> +++ b/arch/arm64/include/asm/memory.h
> @@ -133,6 +133,8 @@
>
> #define IRQ_STACK_SIZE THREAD_SIZE
>
> +#define OVERFLOW_STACK_SIZE SZ_4K
> +
> /*
> * Alignment of kernel segments (e.g. .text, .data).
> */
> diff --git a/arch/arm64/include/asm/stacktrace.h b/arch/arm64/include/asm/stacktrace.h
> index 92ddb6d..ee19563 100644
> --- a/arch/arm64/include/asm/stacktrace.h
> +++ b/arch/arm64/include/asm/stacktrace.h
> @@ -57,6 +57,22 @@ static inline bool on_task_stack(struct task_struct *tsk, unsigned long sp)
> return (low <= sp && sp < high);
> }
>
> +#ifdef CONFIG_VMAP_STACK
> +DECLARE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], overflow_stack);
> +
> +#define OVERFLOW_STACK_PTR() ((unsigned long)this_cpu_ptr(overflow_stack) + OVERFLOW_STACK_SIZE)
> +
> +static inline bool on_overflow_stack(unsigned long sp)
> +{
> + unsigned long low = (unsigned long)this_cpu_ptr(overflow_stack);
Can you use raw_cpu_ptr here, like you do for the irq stack?
> + unsigned long high = low + OVERFLOW_STACK_SIZE;
> +
> + return (low <= sp && sp < high);
> +}
> +#else
> +static inline bool on_overflow_stack(unsigned long sp) { return false; }
> +#endif
> +
> /*
> * We can only safely access per-cpu stacks from current in a non-preemptible
> * context.
> @@ -69,6 +85,8 @@ static inline bool on_accessible_stack(struct task_struct *tsk, unsigned long sp
> return false;
> if (on_irq_stack(sp))
> return true;
> + if (on_overflow_stack(sp))
> + return true;
I find the "return false" clause in this function makes it fiddly to
read because it's really predicating all following conditionals on current
&& !preemptible, but I haven't got any better ideas :(
> return false;
> }
> diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
> index e5aa866..44a27c3 100644
> --- a/arch/arm64/kernel/entry.S
> +++ b/arch/arm64/kernel/entry.S
> @@ -72,6 +72,37 @@
> .macro kernel_ventry label
> .align 7
> sub sp, sp, #S_FRAME_SIZE
> +#ifdef CONFIG_VMAP_STACK
> + add sp, sp, x0 // sp' = sp + x0
> + sub x0, sp, x0 // x0' = sp' - x0 = (sp + x0) - x0 = sp
> + tbnz x0, #THREAD_SHIFT, 0f
> + sub x0, sp, x0 // sp' - x0' = (sp + x0) - sp = x0
> + sub sp, sp, x0 // sp' - x0 = (sp + x0) - x0 = sp
> + b \label
> +
> + /* Stash the original SP value in tpidr_el0 */
> +0: msr tpidr_el0, x0
The comment here is a bit confusing, since the sp has already been
decremented for the frame, as mention in a later comment.
> +
> + /* Recover the original x0 value and stash it in tpidrro_el0 */
> + sub x0, sp, x0
> + msr tpidrro_el0, x0
> +
> + /* Switch to the overflow stack */
> + adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0
> +
> + /*
> + * Check whether we were already on the overflow stack. This may happen
> + * after panic() re-enables interrupts.
> + */
> + mrs x0, tpidr_el0 // sp of interrupted context
> + sub x0, sp, x0 // delta with top of overflow stack
> + tst x0, #~(OVERFLOW_STACK_SIZE - 1) // within range?
> + b.ne __bad_stack // no? -> bad stack pointer
> +
> + /* We were already on the overflow stack. Restore sp/x0 and carry on. */
> + sub sp, sp, x0
> + mrs x0, tpidrro_el0
> +#endif
> b \label
> .endm
>
> @@ -348,6 +379,34 @@ ENTRY(vectors)
> #endif
> END(vectors)
>
> +#ifdef CONFIG_VMAP_STACK
> + /*
> + * We detected an overflow in kernel_ventry, which switched to the
> + * overflow stack. Stash the exception regs, and head to our overflow
> + * handler.
> + */
> +__bad_stack:
> + /* Restore the original x0 value */
> + mrs x0, tpidrro_el0
> +
> + /*
> + * Store the original GPRs to the new stack. The orginial SP (minus
original
> + * S_FRAME_SIZE) was stashed in tpidr_el0 by kernel_ventry.
> + */
> + sub sp, sp, #S_FRAME_SIZE
> + kernel_entry 1
> + mrs x0, tpidr_el0
> + add x0, x0, #S_FRAME_SIZE
> + str x0, [sp, #S_SP]
> +
> + /* Stash the regs for handle_bad_stack */
> + mov x0, sp
> +
> + /* Time to die */
> + bl handle_bad_stack
> + ASM_BUG()
Why not just a b without the ASM_BUG?
Will
next prev parent reply other threads:[~2017-08-14 15:32 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-08-07 18:35 [PATCH 00/14] arm64: VMAP_STACK support Mark Rutland
2017-08-07 18:35 ` [PATCH 01/14] arm64: remove __die()'s stack dump Mark Rutland
2017-08-07 18:35 ` [PATCH 02/14] fork: allow arch-override of VMAP stack alignment Mark Rutland
2017-08-07 18:35 ` [PATCH 03/14] arm64: kernel: remove {THREAD,IRQ_STACK}_START_SP Mark Rutland
2017-08-07 18:35 ` [PATCH 04/14] arm64: factor out PAGE_* and CONT_* definitions Mark Rutland
2017-08-07 18:35 ` [PATCH 05/14] arm64: clean up THREAD_* definitions Mark Rutland
2017-08-14 11:59 ` Catalin Marinas
2017-08-14 13:10 ` Mark Rutland
2017-08-07 18:35 ` [PATCH 06/14] arm64: clean up irq stack definitions Mark Rutland
2017-08-07 18:35 ` [PATCH 07/14] arm64: move SEGMENT_ALIGN to <asm/memory.h> Mark Rutland
2017-08-07 18:35 ` [PATCH 08/14] efi/arm64: add EFI_KIMG_ALIGN Mark Rutland
2017-08-07 18:36 ` [PATCH 09/14] arm64: factor out entry stack manipulation Mark Rutland
2017-08-07 18:36 ` [PATCH 10/14] arm64: assembler: allow adr_this_cpu to use the stack pointer Mark Rutland
2017-08-14 17:13 ` Catalin Marinas
2017-08-14 17:42 ` Mark Rutland
2017-08-07 18:36 ` [PATCH 11/14] arm64: use an irq " Mark Rutland
2017-08-07 18:36 ` [PATCH 12/14] arm64: add basic VMAP_STACK support Mark Rutland
2017-08-07 18:36 ` [PATCH 13/14] arm64: add on_accessible_stack() Mark Rutland
2017-08-07 18:36 ` [PATCH 14/14] arm64: add VMAP_STACK overflow detection Mark Rutland
2017-08-14 15:32 ` Will Deacon [this message]
2017-08-14 17:25 ` Mark Rutland
2017-08-15 11:10 ` Catalin Marinas
2017-08-15 11:19 ` Mark Rutland
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20170814153253.GA22747@arm.com \
--to=will.deacon@arm.com \
--cc=linux-arm-kernel@lists.infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).