core/signals changes for v4.7
Linus,
Please pull the latest core-signals-for-linus git tree from:
git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git core-signals-for-linus
# HEAD: 91c6180572e2fec71701d646ffc40ad30986275c signals/sigaltstack: Change SS_AUTODISARM to (1U << 31)
This tree, from Stas Sergeev and Andy Lutomirski, improves the sigaltstack interface by extending its ABI with the SS_AUTODISARM feature, which makes it possible to use swapcontext() in a sighandler that works on sigaltstack. Without this flag, the subsequent signal will corrupt the state of the switched-away sighandler.
The inspiration is more robust dosemu signal handling.
Thanks,
Ingo
------------------>
Andy Lutomirski (4):
signals/sigaltstack: If SS_AUTODISARM, bypass on_sig_stack() selftests/sigaltstack: Fix the sigaltstack test on old kernels signals/sigaltstack: Report current flag bits in sigaltstack() signals/sigaltstack: Change SS_AUTODISARM to (1U << 31)
Stas Sergeev (4):
signals/sigaltstack, x86/signals: Unify the x86 sigaltstack check with other architectures signals/sigaltstack: Prepare to add new SS_xxx flags
signals/sigaltstack: Implement SS_AUTODISARM flag
selftests/sigaltstack: Add new testcase for sigaltstack(SS_ONSTACK|SS_AUTODISARM)
arch/x86/kernel/signal.c | 23 ++-- include/linux/sched.h | 20 +++ include/linux/signal.h | 4 +- include/uapi/linux/signal.h | 5 + kernel/fork.c | 2 +- kernel/signal.c | 29 +++-- tools/testing/selftests/Makefile | 1 + tools/testing/selftests/sigaltstack/Makefile | 8 ++ tools/testing/selftests/sigaltstack/sas.c | 176 +++++++++++++++++++++++++++ 9 files changed, 241 insertions(+), 27 deletions(-) create mode 100644 tools/testing/selftests/sigaltstack/Makefile create mode 100644 tools/testing/selftests/sigaltstack/sas.c
arch/x86/kernel/signal.c +11 -12
@@ -248,18 +248,17 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size,if (config_enabled (CONFIG_X86_64 )) sp -=128 ; -if (!onsigstack) { -/* This is the X/Open sanctioned signal stack switching. */ -if (ka->sa.sa_flags &SA_ONSTACK ) { -if (current->sas_ss_size) - sp = current->sas_ss_sp + current->sas_ss_size; - }else if (config_enabled (CONFIG_X86_32 ) && - (regs->ss &0xffff ) != __USER_DS && - !(ka->sa.sa_flags &SA_RESTORER ) && - ka->sa.sa_restorer) { -/* This is the legacy signal stack switching. */ - sp = (unsigned long ) ka->sa.sa_restorer; - } +/* This is the X/Open sanctioned signal stack switching. */ +if (ka->sa.sa_flags &SA_ONSTACK ) { +if (sas_ss_flags (sp) ==0 ) + sp = current->sas_ss_sp + current->sas_ss_size; + }else if (config_enabled (CONFIG_X86_32 ) && + !onsigstack && + (regs->ss &0xffff ) != __USER_DS && + !(ka->sa.sa_flags &SA_RESTORER ) && + ka->sa.sa_restorer) { +/* This is the legacy signal stack switching. */ + sp = (unsigned long ) ka->sa.sa_restorer; }if (fpu->fpstate_active) {
include/linux/sched.h +20 -0
@@ -1596,6 +1596,7 @@ struct task_struct {unsigned long sas_ss_sp;size_t sas_ss_size; +unsigned sas_ss_flags;struct callback_head *task_works;
@@ -2575,6 +2576,18 @@ static inline int kill_cad_pid(int sig, int priv)*/ static inline int on_sig_stack (unsigned long sp) { +/* +* If the signal stack is SS_AUTODISARM then, by construction, we +* can't be on the signal stack unless user code deliberately set +* SS_AUTODISARM when we were already on it. +* +* This improves reliability: if user state gets corrupted such that +* the stack pointer points very close to the end of the signal stack, +* then this check will enable the signal to be handled anyway. +*/ +if (current->sas_ss_flags &SS_AUTODISARM ) +return 0 ; +#ifdef CONFIG_STACK_GROWSUP return sp >= current->sas_ss_sp && sp - current->sas_ss_sp < current->sas_ss_size;
@@ -2592,6 +2605,13 @@ static inline int sas_ss_flags(unsigned long sp)return on_sig_stack (sp) ?SS_ONSTACK :0 ; } +static inline void sas_ss_reset (struct task_struct *p) +{ + p->sas_ss_sp =0 ; + p->sas_ss_size =0 ; + p->sas_ss_flags =SS_DISABLE ; +} +static inline unsigned long sigsp (unsigned long sp,struct ksignal *ksig) {if (unlikely ((ksig->ka.sa.sa_flags &SA_ONSTACK )) && !sas_ss_flags (sp))
include/linux/signal.h +3 -1
@@ -432,8 +432,10 @@ int __save_altstack(stack_t __user *, unsigned long); stack_t __user *__uss = uss; \struct task_struct *t = current; \ put_user_ex((void __user *)t->sas_ss_sp, &__uss->ss_sp); \ -put_user_ex (sas_ss_flags (sp), &__uss->ss_flags); \ +put_user_ex (t->sas_ss_flags, &__uss->ss_flags); \put_user_ex (t->sas_ss_size, &__uss->ss_size); \ +if (t->sas_ss_flags &SS_AUTODISARM ) \ +sas_ss_reset (t); \ }while (0 );#ifdef CONFIG_PROC_FS
include/uapi/linux/signal.h +5 -0
@@ -7,4 +7,9 @@#define SS_ONSTACK 1#define SS_DISABLE 2 +/* bit-flags */ +#define SS_AUTODISARM (1U << 31)/* disable sas during sighandling */ +/* mask for all SS_xxx flags */ +#define SS_FLAG_BITS SS_AUTODISARM +#endif /* _UAPI_LINUX_SIGNAL_H */
kernel/fork.c +1 -1
@@ -1494,7 +1494,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,* sigaltstack should be cleared when sharing the same VM */ if ((clone_flags & (CLONE_VM |CLONE_VFORK )) ==CLONE_VM ) - p->sas_ss_sp = p->sas_ss_size =0 ; +sas_ss_reset (p);/* * Syscall tracing and stepping should be turned off in the
kernel/signal.c +16 -13
@@ -3099,12 +3099,14 @@ do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long s oss.ss_sp = (void __user *) current->sas_ss_sp; oss.ss_size = current->sas_ss_size; - oss.ss_flags =sas_ss_flags (sp); + oss.ss_flags =sas_ss_flags (sp) | + (current->sas_ss_flags &SS_FLAG_BITS );if (uss) {void __user *ss_sp;size_t ss_size; -int ss_flags; +unsigned ss_flags; +int ss_mode; error = -EFAULT ;if (!access_ok (VERIFY_READ , uss,sizeof (*uss)))
@@ -3119,18 +3121,13 @@ do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long sif (on_sig_stack (sp)) goto out; + ss_mode = ss_flags & ~SS_FLAG_BITS ; error = -EINVAL ; -/* -* Note - this code used to test ss_flags incorrectly: -* old code may have been written using ss_flags==0 -* to mean ss_flags==SS_ONSTACK (as this was the only -* way that worked) - this fix preserves that older -* mechanism. -*/ -if (ss_flags !=SS_DISABLE && ss_flags !=SS_ONSTACK && ss_flags !=0 ) +if (ss_mode !=SS_DISABLE && ss_mode !=SS_ONSTACK && + ss_mode !=0 ) goto out; -if (ss_flags ==SS_DISABLE ) { +if (ss_mode ==SS_DISABLE ) { ss_size =0 ; ss_sp =NULL ; }else {
@@ -3141,6 +3138,7 @@ do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long s current->sas_ss_sp = (unsigned long ) ss_sp; current->sas_ss_size = ss_size; + current->sas_ss_flags = ss_flags; } error =0 ;
@@ -3171,9 +3169,14 @@ int restore_altstack(const stack_t __user *uss)int __save_altstack (stack_t __user *uss,unsigned long sp) {struct task_struct *t = current; -return __put_user ((void __user *)t->sas_ss_sp, &uss->ss_sp) | -__put_user (sas_ss_flags (sp), &uss->ss_flags) | +int err =__put_user ((void __user *)t->sas_ss_sp, &uss->ss_sp) | +__put_user (t->sas_ss_flags, &uss->ss_flags) |__put_user (t->sas_ss_size, &uss->ss_size); +if (err) +return err; +if (t->sas_ss_flags &SS_AUTODISARM ) +sas_ss_reset (t); +return 0 ; }#ifdef CONFIG_COMPAT
tools/testing/selftests/Makefile +1 -0
@@ -19,6 +19,7 @@ TARGETS += powerpc TARGETS += pstore TARGETS += ptrace TARGETS += seccomp +TARGETS += sigaltstack TARGETS += size TARGETS += static_keys TARGETS += sysctl
tools/testing/selftests/sigaltstack/Makefile new file +8 -0
@@ -0,0 +1,8 @@ +CFLAGS = -Wall +BINARIES = sas +all: $(BINARIES) + +include ../lib.mk + +clean: + rm -rf $(BINARIES)
tools/testing/selftests/sigaltstack/sas.c new file +176 -0
176 lines not on this page · raw message