提交 2beb4a53 编写于 作者: C Chang S. Bae 提交者: Borislav Petkov

x86/signal: Detect and prevent an alternate signal stack overflow

The kernel pushes context on to the userspace stack to prepare for the
user's signal handler. When the user has supplied an alternate signal
stack, via sigaltstack(2), it is easy for the kernel to verify that the
stack size is sufficient for the current hardware context.

Check if writing the hardware context to the alternate stack will exceed
it's size. If yes, then instead of corrupting user-data and proceeding with
the original signal handler, an immediate SIGSEGV signal is delivered.

Refactor the stack pointer check code from on_sig_stack() and use the new
helper.

While the kernel allows new source code to discover and use a sufficient
alternate signal stack size, this check is still necessary to protect
binaries with insufficient alternate signal stack size from data
corruption.

Fixes: c2bc11f1 ("x86, AVX-512: Enable AVX-512 States Context Switch")
Reported-by: NFlorian Weimer <fweimer@redhat.com>
Suggested-by: NJann Horn <jannh@google.com>
Suggested-by: NAndy Lutomirski <luto@kernel.org>
Signed-off-by: NChang S. Bae <chang.seok.bae@intel.com>
Signed-off-by: NBorislav Petkov <bp@suse.de>
Reviewed-by: NLen Brown <len.brown@intel.com>
Acked-by: NThomas Gleixner <tglx@linutronix.de>
Link: https://lkml.kernel.org/r/20210518200320.17239-6-chang.seok.bae@intel.com
Link: https://bugzilla.kernel.org/show_bug.cgi?id=153531
上级 bdf6c8b8
...@@ -239,10 +239,11 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size, ...@@ -239,10 +239,11 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size,
void __user **fpstate) void __user **fpstate)
{ {
/* Default to using normal stack */ /* Default to using normal stack */
bool nested_altstack = on_sig_stack(regs->sp);
bool entering_altstack = false;
unsigned long math_size = 0; unsigned long math_size = 0;
unsigned long sp = regs->sp; unsigned long sp = regs->sp;
unsigned long buf_fx = 0; unsigned long buf_fx = 0;
int onsigstack = on_sig_stack(sp);
int ret; int ret;
/* redzone */ /* redzone */
...@@ -251,15 +252,23 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size, ...@@ -251,15 +252,23 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size,
/* This is the X/Open sanctioned signal stack switching. */ /* This is the X/Open sanctioned signal stack switching. */
if (ka->sa.sa_flags & SA_ONSTACK) { if (ka->sa.sa_flags & SA_ONSTACK) {
if (sas_ss_flags(sp) == 0) /*
* This checks nested_altstack via sas_ss_flags(). Sensible
* programs use SS_AUTODISARM, which disables that check, and
* programs that don't use SS_AUTODISARM get compatible.
*/
if (sas_ss_flags(sp) == 0) {
sp = current->sas_ss_sp + current->sas_ss_size; sp = current->sas_ss_sp + current->sas_ss_size;
entering_altstack = true;
}
} else if (IS_ENABLED(CONFIG_X86_32) && } else if (IS_ENABLED(CONFIG_X86_32) &&
!onsigstack && !nested_altstack &&
regs->ss != __USER_DS && regs->ss != __USER_DS &&
!(ka->sa.sa_flags & SA_RESTORER) && !(ka->sa.sa_flags & SA_RESTORER) &&
ka->sa.sa_restorer) { ka->sa.sa_restorer) {
/* This is the legacy signal stack switching. */ /* This is the legacy signal stack switching. */
sp = (unsigned long) ka->sa.sa_restorer; sp = (unsigned long) ka->sa.sa_restorer;
entering_altstack = true;
} }
sp = fpu__alloc_mathframe(sp, IS_ENABLED(CONFIG_X86_32), sp = fpu__alloc_mathframe(sp, IS_ENABLED(CONFIG_X86_32),
...@@ -272,8 +281,15 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size, ...@@ -272,8 +281,15 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size,
* If we are on the alternate signal stack and would overflow it, don't. * If we are on the alternate signal stack and would overflow it, don't.
* Return an always-bogus address instead so we will die with SIGSEGV. * Return an always-bogus address instead so we will die with SIGSEGV.
*/ */
if (onsigstack && !likely(on_sig_stack(sp))) if (unlikely((nested_altstack || entering_altstack) &&
!__on_sig_stack(sp))) {
if (show_unhandled_signals && printk_ratelimit())
pr_info("%s[%d] overflowed sigaltstack\n",
current->comm, task_pid_nr(current));
return (void __user *)-1L; return (void __user *)-1L;
}
/* save i387 and extended state */ /* save i387 and extended state */
ret = copy_fpstate_to_sigframe(*fpstate, (void __user *)buf_fx, math_size); ret = copy_fpstate_to_sigframe(*fpstate, (void __user *)buf_fx, math_size);
......
...@@ -537,6 +537,17 @@ static inline int kill_cad_pid(int sig, int priv) ...@@ -537,6 +537,17 @@ static inline int kill_cad_pid(int sig, int priv)
#define SEND_SIG_NOINFO ((struct kernel_siginfo *) 0) #define SEND_SIG_NOINFO ((struct kernel_siginfo *) 0)
#define SEND_SIG_PRIV ((struct kernel_siginfo *) 1) #define SEND_SIG_PRIV ((struct kernel_siginfo *) 1)
static inline int __on_sig_stack(unsigned long sp)
{
#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
}
/* /*
* True if we are on the alternate signal stack. * True if we are on the alternate signal stack.
*/ */
...@@ -554,13 +565,7 @@ static inline int on_sig_stack(unsigned long sp) ...@@ -554,13 +565,7 @@ static inline int on_sig_stack(unsigned long sp)
if (current->sas_ss_flags & SS_AUTODISARM) if (current->sas_ss_flags & SS_AUTODISARM)
return 0; return 0;
#ifdef CONFIG_STACK_GROWSUP return __on_sig_stack(sp);
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) static inline int sas_ss_flags(unsigned long sp)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册