| From 2a855dd01bc1539111adb7233f587c5c468732ac Mon Sep 17 00:00:00 2001 |
| From: Sebastian Andrzej Siewior <sebastian@breakpoint.cc> |
| Date: Sun, 25 Oct 2009 15:37:58 +0100 |
| Subject: signal: Fix alternate signal stack check |
| |
| From: Sebastian Andrzej Siewior <sebastian@breakpoint.cc> |
| |
| commit 2a855dd01bc1539111adb7233f587c5c468732ac upstream. |
| |
| All architectures in the kernel increment/decrement the stack pointer |
| before storing values on the stack. |
| |
| On architectures which have the stack grow down sas_ss_sp == sp is not |
| on the alternate signal stack while sas_ss_sp + sas_ss_size == sp is |
| on the alternate signal stack. |
| |
| On architectures which have the stack grow up sas_ss_sp == sp is on |
| the alternate signal stack while sas_ss_sp + sas_ss_size == sp is not |
| on the alternate signal stack. |
| |
| The current implementation fails for architectures which have the |
| stack grow down on the corner case where sas_ss_sp == sp.This was |
| reported as Debian bug #544905 on AMD64. |
| Simplified test case: http://download.breakpoint.cc/tc-sig-stack.c |
| |
| The test case creates the following stack scenario: |
| 0xn0300 stack top |
| 0xn0200 alt stack pointer top (when switching to alt stack) |
| 0xn01ff alt stack end |
| 0xn0100 alt stack start == stack pointer |
| |
| If the signal is sent the stack pointer is pointing to the base |
| address of the alt stack and the kernel erroneously decides that it |
| has already switched to the alternate stack because of the current |
| check for "sp - sas_ss_sp < sas_ss_size" |
| |
| On parisc (stack grows up) the scenario would be: |
| 0xn0200 stack pointer |
| 0xn01ff alt stack end |
| 0xn0100 alt stack start = alt stack pointer base |
| (when switching to alt stack) |
| 0xn0000 stack base |
| |
| This is handled correctly by the current implementation. |
| |
| [ tglx: Modified for archs which have the stack grow up (parisc) which |
| would fail with the correct implementation for stack grows |
| down. Added a check for sp >= current->sas_ss_sp which is |
| strictly not necessary but makes the code symetric for both |
| variants ] |
| |
| Signed-off-by: Sebastian Andrzej Siewior <sebastian@breakpoint.cc> |
| Cc: Oleg Nesterov <oleg@redhat.com> |
| Cc: Roland McGrath <roland@redhat.com> |
| Cc: Kyle McMartin <kyle@mcmartin.ca> |
| LKML-Reference: <20091025143758.GA6653@Chamillionaire.breakpoint.cc> |
| Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| include/linux/sched.h | 13 ++++++++++--- |
| 1 file changed, 10 insertions(+), 3 deletions(-) |
| |
| --- a/include/linux/sched.h |
| +++ b/include/linux/sched.h |
| @@ -1999,11 +1999,18 @@ static inline int is_si_special(const st |
| return info <= SEND_SIG_FORCED; |
| } |
| |
| -/* True if we are on the alternate signal stack. */ |
| - |
| +/* |
| + * True if we are on the alternate signal stack. |
| + */ |
| static inline int on_sig_stack(unsigned long sp) |
| { |
| - return (sp - current->sas_ss_sp < current->sas_ss_size); |
| +#ifdef CONFIG_STACK_GROWSUP |
| + return sp >= current->sas_ss_sp && |
| + sp - current->sas_ss_sp < current->sas_ss_size; |
| +#else |
| + return sp > current->sas_ss_sp && |
| + sp - current->sas_ss_sp <= current->sas_ss_size; |
| +#endif |
| } |
| |
| static inline int sas_ss_flags(unsigned long sp) |