aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel
diff options
context:
space:
mode:
authorAndy Lutomirski <luto@kernel.org>2016-02-16 18:09:03 -0500
committerIngo Molnar <mingo@kernel.org>2016-02-17 02:32:11 -0500
commit6c25da5ad55d48c41b8909bc1f4e3cd5d85bb499 (patch)
tree3dc78e9e4f4b59fa3523536e5a00f3c5e0579126 /arch/x86/kernel
parent8ff5bd2e1e2767fbf737f84d5f92668dafe7e7b0 (diff)
x86/signal/64: Re-add support for SS in the 64-bit signal context
This is a second attempt to make the improvements from c6f2062935c8 ("x86/signal/64: Fix SS handling for signals delivered to 64-bit programs"), which was reverted by 51adbfbba5c6 ("x86/signal/64: Add support for SS in the 64-bit signal context"). This adds two new uc_flags flags. UC_SIGCONTEXT_SS will be set for all 64-bit signals (including x32). It indicates that the saved SS field is valid and that the kernel supports the new behavior. The goal is to fix a problems with signal handling in 64-bit tasks: SS wasn't saved in the 64-bit signal context, making it awkward to determine what SS was at the time of signal delivery and making it impossible to return to a non-flat SS (as calling sigreturn clobbers SS). This also made it extremely difficult for 64-bit tasks to return to fully-defined 16-bit contexts, because only the kernel can easily do espfix64, but sigreturn was unable to set a non-flag SS:ESP. (DOSEMU has a monstrous hack to partially work around this limitation.) If we could go back in time, the correct fix would be to make 64-bit signals work just like 32-bit signals with respect to SS: save it in signal context, reset it when delivering a signal, and restore it in sigreturn. Unfortunately, doing that (as I tried originally) breaks DOSEMU: DOSEMU wouldn't reset the signal context's SS when clearing the LDT and changing the saved CS to 64-bit mode, since it predates the SS context field existing in the first place. This patch is a bit more complicated, and it tries to balance a bunch of goals. It makes most cases of changing ucontext->ss during signal handling work as expected. I do this by special-casing the interesting case. On sigreturn, ucontext->ss will be honored by default, unless the ucontext was created from scratch by an old program and had a 64-bit CS (unfortunately, CRIU can do this) or was the result of changing a 32-bit signal context to 64-bit without resetting SS (as DOSEMU does). For the benefit of new 64-bit software that uses segmentation (new versions of DOSEMU might), the new behavior can be detected with a new ucontext flag UC_SIGCONTEXT_SS. To avoid compilation issues, __pad0 is left as an alias for ss in ucontext. The nitty-gritty details are documented in the header file. This patch also re-enables the sigreturn_64 and ldt_gdt_64 selftests, as the kernel change allows both of them to pass. Tested-by: Stas Sergeev <stsp@list.ru> Signed-off-by: Andy Lutomirski <luto@kernel.org> Acked-by: Borislav Petkov <bp@alien8.de> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: Andy Lutomirski <luto@amacapital.net> Cc: Brian Gerst <brgerst@gmail.com> Cc: Cyrill Gorcunov <gorcunov@gmail.com> Cc: Denys Vlasenko <dvlasenk@redhat.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Pavel Emelyanov <xemul@parallels.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Link: http://lkml.kernel.org/r/749149cbfc3e75cd7fcdad69a854b399d792cc6f.1455664054.git.luto@kernel.org [ Small readability edit. ] Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'arch/x86/kernel')
-rw-r--r--arch/x86/kernel/signal.c63
1 files changed, 44 insertions, 19 deletions
diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
index 52f82c7ef57d..548ddf7d6fd2 100644
--- a/arch/x86/kernel/signal.c
+++ b/arch/x86/kernel/signal.c
@@ -90,7 +90,9 @@ static void force_valid_ss(struct pt_regs *regs)
90} 90}
91#endif 91#endif
92 92
93int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) 93static int restore_sigcontext(struct pt_regs *regs,
94 struct sigcontext __user *sc,
95 unsigned long uc_flags)
94{ 96{
95 unsigned long buf_val; 97 unsigned long buf_val;
96 void __user *buf; 98 void __user *buf;
@@ -123,15 +125,18 @@ int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
123 COPY(r15); 125 COPY(r15);
124#endif /* CONFIG_X86_64 */ 126#endif /* CONFIG_X86_64 */
125 127
126#ifdef CONFIG_X86_32
127 COPY_SEG_CPL3(cs); 128 COPY_SEG_CPL3(cs);
128 COPY_SEG_CPL3(ss); 129 COPY_SEG_CPL3(ss);
129#else /* !CONFIG_X86_32 */ 130
130 /* Kernel saves and restores only the CS segment register on signals, 131#ifdef CONFIG_X86_64
131 * which is the bare minimum needed to allow mixed 32/64-bit code. 132 /*
132 * App's signal handler can save/restore other segments if needed. */ 133 * Fix up SS if needed for the benefit of old DOSEMU and
133 COPY_SEG_CPL3(cs); 134 * CRIU.
134#endif /* CONFIG_X86_32 */ 135 */
136 if (unlikely(!(uc_flags & UC_STRICT_RESTORE_SS) &&
137 user_64bit_mode(regs)))
138 force_valid_ss(regs);
139#endif
135 140
136 get_user_ex(tmpflags, &sc->flags); 141 get_user_ex(tmpflags, &sc->flags);
137 regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS); 142 regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS);
@@ -194,6 +199,7 @@ int setup_sigcontext(struct sigcontext __user *sc, void __user *fpstate,
194 put_user_ex(regs->cs, &sc->cs); 199 put_user_ex(regs->cs, &sc->cs);
195 put_user_ex(0, &sc->gs); 200 put_user_ex(0, &sc->gs);
196 put_user_ex(0, &sc->fs); 201 put_user_ex(0, &sc->fs);
202 put_user_ex(regs->ss, &sc->ss);
197#endif /* CONFIG_X86_32 */ 203#endif /* CONFIG_X86_32 */
198 204
199 put_user_ex(fpstate, &sc->fpstate); 205 put_user_ex(fpstate, &sc->fpstate);
@@ -432,6 +438,21 @@ static int __setup_rt_frame(int sig, struct ksignal *ksig,
432 return 0; 438 return 0;
433} 439}
434#else /* !CONFIG_X86_32 */ 440#else /* !CONFIG_X86_32 */
441static unsigned long frame_uc_flags(struct pt_regs *regs)
442{
443 unsigned long flags;
444
445 if (cpu_has_xsave)
446 flags = UC_FP_XSTATE | UC_SIGCONTEXT_SS;
447 else
448 flags = UC_SIGCONTEXT_SS;
449
450 if (likely(user_64bit_mode(regs)))
451 flags |= UC_STRICT_RESTORE_SS;
452
453 return flags;
454}
455
435static int __setup_rt_frame(int sig, struct ksignal *ksig, 456static int __setup_rt_frame(int sig, struct ksignal *ksig,
436 sigset_t *set, struct pt_regs *regs) 457 sigset_t *set, struct pt_regs *regs)
437{ 458{
@@ -451,10 +472,7 @@ static int __setup_rt_frame(int sig, struct ksignal *ksig,
451 472
452 put_user_try { 473 put_user_try {
453 /* Create the ucontext. */ 474 /* Create the ucontext. */
454 if (cpu_has_xsave) 475 put_user_ex(frame_uc_flags(regs), &frame->uc.uc_flags);
455 put_user_ex(UC_FP_XSTATE, &frame->uc.uc_flags);
456 else
457 put_user_ex(0, &frame->uc.uc_flags);
458 put_user_ex(0, &frame->uc.uc_link); 476 put_user_ex(0, &frame->uc.uc_link);
459 save_altstack_ex(&frame->uc.uc_stack, regs->sp); 477 save_altstack_ex(&frame->uc.uc_stack, regs->sp);
460 478
@@ -536,10 +554,7 @@ static int x32_setup_rt_frame(struct ksignal *ksig,
536 554
537 put_user_try { 555 put_user_try {
538 /* Create the ucontext. */ 556 /* Create the ucontext. */
539 if (cpu_has_xsave) 557 put_user_ex(frame_uc_flags(regs), &frame->uc.uc_flags);
540 put_user_ex(UC_FP_XSTATE, &frame->uc.uc_flags);
541 else
542 put_user_ex(0, &frame->uc.uc_flags);
543 put_user_ex(0, &frame->uc.uc_link); 558 put_user_ex(0, &frame->uc.uc_link);
544 compat_save_altstack_ex(&frame->uc.uc_stack, regs->sp); 559 compat_save_altstack_ex(&frame->uc.uc_stack, regs->sp);
545 put_user_ex(0, &frame->uc.uc__pad0); 560 put_user_ex(0, &frame->uc.uc__pad0);
@@ -601,7 +616,11 @@ asmlinkage unsigned long sys_sigreturn(void)
601 616
602 set_current_blocked(&set); 617 set_current_blocked(&set);
603 618
604 if (restore_sigcontext(regs, &frame->sc)) 619 /*
620 * x86_32 has no uc_flags bits relevant to restore_sigcontext.
621 * Save a few cycles by skipping the __get_user.
622 */
623 if (restore_sigcontext(regs, &frame->sc, 0))
605 goto badframe; 624 goto badframe;
606 return regs->ax; 625 return regs->ax;
607 626
@@ -617,16 +636,19 @@ asmlinkage long sys_rt_sigreturn(void)
617 struct pt_regs *regs = current_pt_regs(); 636 struct pt_regs *regs = current_pt_regs();
618 struct rt_sigframe __user *frame; 637 struct rt_sigframe __user *frame;
619 sigset_t set; 638 sigset_t set;
639 unsigned long uc_flags;
620 640
621 frame = (struct rt_sigframe __user *)(regs->sp - sizeof(long)); 641 frame = (struct rt_sigframe __user *)(regs->sp - sizeof(long));
622 if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) 642 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
623 goto badframe; 643 goto badframe;
624 if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) 644 if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
625 goto badframe; 645 goto badframe;
646 if (__get_user(uc_flags, &frame->uc.uc_flags))
647 goto badframe;
626 648
627 set_current_blocked(&set); 649 set_current_blocked(&set);
628 650
629 if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) 651 if (restore_sigcontext(regs, &frame->uc.uc_mcontext, uc_flags))
630 goto badframe; 652 goto badframe;
631 653
632 if (restore_altstack(&frame->uc.uc_stack)) 654 if (restore_altstack(&frame->uc.uc_stack))
@@ -813,6 +835,7 @@ asmlinkage long sys32_x32_rt_sigreturn(void)
813 struct pt_regs *regs = current_pt_regs(); 835 struct pt_regs *regs = current_pt_regs();
814 struct rt_sigframe_x32 __user *frame; 836 struct rt_sigframe_x32 __user *frame;
815 sigset_t set; 837 sigset_t set;
838 unsigned long uc_flags;
816 839
817 frame = (struct rt_sigframe_x32 __user *)(regs->sp - 8); 840 frame = (struct rt_sigframe_x32 __user *)(regs->sp - 8);
818 841
@@ -820,10 +843,12 @@ asmlinkage long sys32_x32_rt_sigreturn(void)
820 goto badframe; 843 goto badframe;
821 if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) 844 if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
822 goto badframe; 845 goto badframe;
846 if (__get_user(uc_flags, &frame->uc.uc_flags))
847 goto badframe;
823 848
824 set_current_blocked(&set); 849 set_current_blocked(&set);
825 850
826 if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) 851 if (restore_sigcontext(regs, &frame->uc.uc_mcontext, uc_flags))
827 goto badframe; 852 goto badframe;
828 853
829 if (compat_restore_altstack(&frame->uc.uc_stack)) 854 if (compat_restore_altstack(&frame->uc.uc_stack))