diff options
author | Stas Sergeev <stsp@list.ru> | 2016-04-14 16:20:04 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2016-05-03 02:37:59 -0400 |
commit | 2a74213838104a41588d86fd5e8d344972891ace (patch) | |
tree | a9cbd4a76cf96295bc943567fbd575af908c5974 | |
parent | 407bc16ad1769f5cb8ad9555611cb198187ef4cd (diff) |
signals/sigaltstack: Implement SS_AUTODISARM flag
This patch implements the SS_AUTODISARM flag that can be OR-ed with
SS_ONSTACK when forming ss_flags.
When this flag is set, sigaltstack will be disabled when entering
the signal handler; more precisely, after saving sas to uc_stack.
When leaving the signal handler, the sigaltstack is restored by
uc_stack.
When this flag is used, it is safe to switch from sighandler with
swapcontext(). Without this flag, the subsequent signal will corrupt
the state of the switched-away sighandler.
To detect the support of this functionality, one can do:
err = sigaltstack(SS_DISABLE | SS_AUTODISARM);
if (err && errno == EINVAL)
unsupported();
Signed-off-by: Stas Sergeev <stsp@list.ru>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Aleksa Sarai <cyphar@cyphar.com>
Cc: Amanieu d'Antras <amanieu@gmail.com>
Cc: Andrea Arcangeli <aarcange@redhat.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: Eric W. Biederman <ebiederm@xmission.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Heinrich Schuchardt <xypron.glpk@gmx.de>
Cc: Jason Low <jason.low2@hp.com>
Cc: Josh Triplett <josh@joshtriplett.org>
Cc: Konstantin Khlebnikov <khlebnikov@yandex-team.ru>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Paul Moore <pmoore@redhat.com>
Cc: Pavel Emelyanov <xemul@parallels.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Richard Weinberger <richard@nod.at>
Cc: Sasha Levin <sasha.levin@oracle.com>
Cc: Shuah Khan <shuahkh@osg.samsung.com>
Cc: Tejun Heo <tj@kernel.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Vladimir Davydov <vdavydov@parallels.com>
Cc: linux-api@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Link: http://lkml.kernel.org/r/1460665206-13646-4-git-send-email-stsp@list.ru
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | include/linux/sched.h | 8 | ||||
-rw-r--r-- | include/linux/signal.h | 4 | ||||
-rw-r--r-- | include/uapi/linux/signal.h | 4 | ||||
-rw-r--r-- | kernel/fork.c | 2 | ||||
-rw-r--r-- | kernel/signal.c | 10 |
5 files changed, 23 insertions, 5 deletions
diff --git a/include/linux/sched.h b/include/linux/sched.h index 52c4847b05e2..2950c5cd3005 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h | |||
@@ -1596,6 +1596,7 @@ struct task_struct { | |||
1596 | 1596 | ||
1597 | unsigned long sas_ss_sp; | 1597 | unsigned long sas_ss_sp; |
1598 | size_t sas_ss_size; | 1598 | size_t sas_ss_size; |
1599 | unsigned sas_ss_flags; | ||
1599 | 1600 | ||
1600 | struct callback_head *task_works; | 1601 | struct callback_head *task_works; |
1601 | 1602 | ||
@@ -2592,6 +2593,13 @@ static inline int sas_ss_flags(unsigned long sp) | |||
2592 | return on_sig_stack(sp) ? SS_ONSTACK : 0; | 2593 | return on_sig_stack(sp) ? SS_ONSTACK : 0; |
2593 | } | 2594 | } |
2594 | 2595 | ||
2596 | static inline void sas_ss_reset(struct task_struct *p) | ||
2597 | { | ||
2598 | p->sas_ss_sp = 0; | ||
2599 | p->sas_ss_size = 0; | ||
2600 | p->sas_ss_flags = SS_DISABLE; | ||
2601 | } | ||
2602 | |||
2595 | static inline unsigned long sigsp(unsigned long sp, struct ksignal *ksig) | 2603 | static inline unsigned long sigsp(unsigned long sp, struct ksignal *ksig) |
2596 | { | 2604 | { |
2597 | if (unlikely((ksig->ka.sa.sa_flags & SA_ONSTACK)) && ! sas_ss_flags(sp)) | 2605 | if (unlikely((ksig->ka.sa.sa_flags & SA_ONSTACK)) && ! sas_ss_flags(sp)) |
diff --git a/include/linux/signal.h b/include/linux/signal.h index 92557bbce7e7..3fbe81444d31 100644 --- a/include/linux/signal.h +++ b/include/linux/signal.h | |||
@@ -432,8 +432,10 @@ int __save_altstack(stack_t __user *, unsigned long); | |||
432 | stack_t __user *__uss = uss; \ | 432 | stack_t __user *__uss = uss; \ |
433 | struct task_struct *t = current; \ | 433 | struct task_struct *t = current; \ |
434 | put_user_ex((void __user *)t->sas_ss_sp, &__uss->ss_sp); \ | 434 | put_user_ex((void __user *)t->sas_ss_sp, &__uss->ss_sp); \ |
435 | put_user_ex(sas_ss_flags(sp), &__uss->ss_flags); \ | 435 | put_user_ex(t->sas_ss_flags, &__uss->ss_flags); \ |
436 | put_user_ex(t->sas_ss_size, &__uss->ss_size); \ | 436 | put_user_ex(t->sas_ss_size, &__uss->ss_size); \ |
437 | if (t->sas_ss_flags & SS_AUTODISARM) \ | ||
438 | sas_ss_reset(t); \ | ||
437 | } while (0); | 439 | } while (0); |
438 | 440 | ||
439 | #ifdef CONFIG_PROC_FS | 441 | #ifdef CONFIG_PROC_FS |
diff --git a/include/uapi/linux/signal.h b/include/uapi/linux/signal.h index 7c73165d11ce..738826048af2 100644 --- a/include/uapi/linux/signal.h +++ b/include/uapi/linux/signal.h | |||
@@ -7,7 +7,9 @@ | |||
7 | #define SS_ONSTACK 1 | 7 | #define SS_ONSTACK 1 |
8 | #define SS_DISABLE 2 | 8 | #define SS_DISABLE 2 |
9 | 9 | ||
10 | /* bit-flags */ | ||
11 | #define SS_AUTODISARM (1 << 4) /* disable sas during sighandling */ | ||
10 | /* mask for all SS_xxx flags */ | 12 | /* mask for all SS_xxx flags */ |
11 | #define SS_FLAG_BITS 0 | 13 | #define SS_FLAG_BITS SS_AUTODISARM |
12 | 14 | ||
13 | #endif /* _UAPI_LINUX_SIGNAL_H */ | 15 | #endif /* _UAPI_LINUX_SIGNAL_H */ |
diff --git a/kernel/fork.c b/kernel/fork.c index d277e83ed3e0..3e8451527cbe 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
@@ -1494,7 +1494,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, | |||
1494 | * sigaltstack should be cleared when sharing the same VM | 1494 | * sigaltstack should be cleared when sharing the same VM |
1495 | */ | 1495 | */ |
1496 | if ((clone_flags & (CLONE_VM|CLONE_VFORK)) == CLONE_VM) | 1496 | if ((clone_flags & (CLONE_VM|CLONE_VFORK)) == CLONE_VM) |
1497 | p->sas_ss_sp = p->sas_ss_size = 0; | 1497 | sas_ss_reset(p); |
1498 | 1498 | ||
1499 | /* | 1499 | /* |
1500 | * Syscall tracing and stepping should be turned off in the | 1500 | * Syscall tracing and stepping should be turned off in the |
diff --git a/kernel/signal.c b/kernel/signal.c index b1c6eb4df2a8..bf97ea5775ae 100644 --- a/kernel/signal.c +++ b/kernel/signal.c | |||
@@ -3137,6 +3137,7 @@ do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long s | |||
3137 | 3137 | ||
3138 | current->sas_ss_sp = (unsigned long) ss_sp; | 3138 | current->sas_ss_sp = (unsigned long) ss_sp; |
3139 | current->sas_ss_size = ss_size; | 3139 | current->sas_ss_size = ss_size; |
3140 | current->sas_ss_flags = ss_flags; | ||
3140 | } | 3141 | } |
3141 | 3142 | ||
3142 | error = 0; | 3143 | error = 0; |
@@ -3167,9 +3168,14 @@ int restore_altstack(const stack_t __user *uss) | |||
3167 | int __save_altstack(stack_t __user *uss, unsigned long sp) | 3168 | int __save_altstack(stack_t __user *uss, unsigned long sp) |
3168 | { | 3169 | { |
3169 | struct task_struct *t = current; | 3170 | struct task_struct *t = current; |
3170 | return __put_user((void __user *)t->sas_ss_sp, &uss->ss_sp) | | 3171 | int err = __put_user((void __user *)t->sas_ss_sp, &uss->ss_sp) | |
3171 | __put_user(sas_ss_flags(sp), &uss->ss_flags) | | 3172 | __put_user(t->sas_ss_flags, &uss->ss_flags) | |
3172 | __put_user(t->sas_ss_size, &uss->ss_size); | 3173 | __put_user(t->sas_ss_size, &uss->ss_size); |
3174 | if (err) | ||
3175 | return err; | ||
3176 | if (t->sas_ss_flags & SS_AUTODISARM) | ||
3177 | sas_ss_reset(t); | ||
3178 | return 0; | ||
3173 | } | 3179 | } |
3174 | 3180 | ||
3175 | #ifdef CONFIG_COMPAT | 3181 | #ifdef CONFIG_COMPAT |