diff options
Diffstat (limited to 'arch/x86/kernel/signal.c')
| -rw-r--r-- | arch/x86/kernel/signal.c | 215 |
1 files changed, 93 insertions, 122 deletions
diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c index b280908a376e..3160c26db5e7 100644 --- a/arch/x86/kernel/signal.c +++ b/arch/x86/kernel/signal.c | |||
| @@ -114,7 +114,7 @@ int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, | |||
| 114 | regs->orig_ax = -1; /* disable syscall checks */ | 114 | regs->orig_ax = -1; /* disable syscall checks */ |
| 115 | 115 | ||
| 116 | get_user_ex(buf, &sc->fpstate); | 116 | get_user_ex(buf, &sc->fpstate); |
| 117 | err |= restore_i387_xstate(buf); | 117 | err |= restore_xstate_sig(buf, config_enabled(CONFIG_X86_32)); |
| 118 | 118 | ||
| 119 | get_user_ex(*pax, &sc->ax); | 119 | get_user_ex(*pax, &sc->ax); |
| 120 | } get_user_catch(err); | 120 | } get_user_catch(err); |
| @@ -206,35 +206,32 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size, | |||
| 206 | void __user **fpstate) | 206 | void __user **fpstate) |
| 207 | { | 207 | { |
| 208 | /* Default to using normal stack */ | 208 | /* Default to using normal stack */ |
| 209 | unsigned long math_size = 0; | ||
| 209 | unsigned long sp = regs->sp; | 210 | unsigned long sp = regs->sp; |
| 211 | unsigned long buf_fx = 0; | ||
| 210 | int onsigstack = on_sig_stack(sp); | 212 | int onsigstack = on_sig_stack(sp); |
| 211 | 213 | ||
| 212 | #ifdef CONFIG_X86_64 | ||
| 213 | /* redzone */ | 214 | /* redzone */ |
| 214 | sp -= 128; | 215 | if (config_enabled(CONFIG_X86_64)) |
| 215 | #endif /* CONFIG_X86_64 */ | 216 | sp -= 128; |
| 216 | 217 | ||
| 217 | if (!onsigstack) { | 218 | if (!onsigstack) { |
| 218 | /* This is the X/Open sanctioned signal stack switching. */ | 219 | /* This is the X/Open sanctioned signal stack switching. */ |
| 219 | if (ka->sa.sa_flags & SA_ONSTACK) { | 220 | if (ka->sa.sa_flags & SA_ONSTACK) { |
| 220 | if (current->sas_ss_size) | 221 | if (current->sas_ss_size) |
| 221 | sp = current->sas_ss_sp + current->sas_ss_size; | 222 | sp = current->sas_ss_sp + current->sas_ss_size; |
| 222 | } else { | 223 | } else if (config_enabled(CONFIG_X86_32) && |
| 223 | #ifdef CONFIG_X86_32 | 224 | (regs->ss & 0xffff) != __USER_DS && |
| 224 | /* This is the legacy signal stack switching. */ | 225 | !(ka->sa.sa_flags & SA_RESTORER) && |
| 225 | if ((regs->ss & 0xffff) != __USER_DS && | 226 | ka->sa.sa_restorer) { |
| 226 | !(ka->sa.sa_flags & SA_RESTORER) && | 227 | /* This is the legacy signal stack switching. */ |
| 227 | ka->sa.sa_restorer) | ||
| 228 | sp = (unsigned long) ka->sa.sa_restorer; | 228 | sp = (unsigned long) ka->sa.sa_restorer; |
| 229 | #endif /* CONFIG_X86_32 */ | ||
| 230 | } | 229 | } |
| 231 | } | 230 | } |
| 232 | 231 | ||
| 233 | if (used_math()) { | 232 | if (used_math()) { |
| 234 | sp -= sig_xstate_size; | 233 | sp = alloc_mathframe(sp, config_enabled(CONFIG_X86_32), |
| 235 | #ifdef CONFIG_X86_64 | 234 | &buf_fx, &math_size); |
| 236 | sp = round_down(sp, 64); | ||
| 237 | #endif /* CONFIG_X86_64 */ | ||
| 238 | *fpstate = (void __user *)sp; | 235 | *fpstate = (void __user *)sp; |
| 239 | } | 236 | } |
| 240 | 237 | ||
| @@ -247,8 +244,9 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size, | |||
| 247 | if (onsigstack && !likely(on_sig_stack(sp))) | 244 | if (onsigstack && !likely(on_sig_stack(sp))) |
| 248 | return (void __user *)-1L; | 245 | return (void __user *)-1L; |
| 249 | 246 | ||
| 250 | /* save i387 state */ | 247 | /* save i387 and extended state */ |
| 251 | if (used_math() && save_i387_xstate(*fpstate) < 0) | 248 | if (used_math() && |
| 249 | save_xstate_sig(*fpstate, (void __user *)buf_fx, math_size) < 0) | ||
| 252 | return (void __user *)-1L; | 250 | return (void __user *)-1L; |
| 253 | 251 | ||
| 254 | return (void __user *)sp; | 252 | return (void __user *)sp; |
| @@ -474,6 +472,74 @@ static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | |||
| 474 | } | 472 | } |
| 475 | #endif /* CONFIG_X86_32 */ | 473 | #endif /* CONFIG_X86_32 */ |
| 476 | 474 | ||
| 475 | static int x32_setup_rt_frame(int sig, struct k_sigaction *ka, | ||
| 476 | siginfo_t *info, compat_sigset_t *set, | ||
| 477 | struct pt_regs *regs) | ||
| 478 | { | ||
| 479 | #ifdef CONFIG_X86_X32_ABI | ||
| 480 | struct rt_sigframe_x32 __user *frame; | ||
| 481 | void __user *restorer; | ||
| 482 | int err = 0; | ||
| 483 | void __user *fpstate = NULL; | ||
| 484 | |||
| 485 | frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate); | ||
| 486 | |||
| 487 | if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) | ||
| 488 | return -EFAULT; | ||
| 489 | |||
| 490 | if (ka->sa.sa_flags & SA_SIGINFO) { | ||
| 491 | if (copy_siginfo_to_user32(&frame->info, info)) | ||
| 492 | return -EFAULT; | ||
| 493 | } | ||
| 494 | |||
| 495 | put_user_try { | ||
| 496 | /* Create the ucontext. */ | ||
| 497 | if (cpu_has_xsave) | ||
| 498 | put_user_ex(UC_FP_XSTATE, &frame->uc.uc_flags); | ||
| 499 | else | ||
| 500 | put_user_ex(0, &frame->uc.uc_flags); | ||
| 501 | put_user_ex(0, &frame->uc.uc_link); | ||
| 502 | put_user_ex(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); | ||
| 503 | put_user_ex(sas_ss_flags(regs->sp), | ||
| 504 | &frame->uc.uc_stack.ss_flags); | ||
| 505 | put_user_ex(current->sas_ss_size, &frame->uc.uc_stack.ss_size); | ||
| 506 | put_user_ex(0, &frame->uc.uc__pad0); | ||
| 507 | err |= setup_sigcontext(&frame->uc.uc_mcontext, fpstate, | ||
| 508 | regs, set->sig[0]); | ||
| 509 | err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); | ||
| 510 | |||
| 511 | if (ka->sa.sa_flags & SA_RESTORER) { | ||
| 512 | restorer = ka->sa.sa_restorer; | ||
| 513 | } else { | ||
| 514 | /* could use a vstub here */ | ||
| 515 | restorer = NULL; | ||
| 516 | err |= -EFAULT; | ||
| 517 | } | ||
| 518 | put_user_ex(restorer, &frame->pretcode); | ||
| 519 | } put_user_catch(err); | ||
| 520 | |||
| 521 | if (err) | ||
| 522 | return -EFAULT; | ||
| 523 | |||
| 524 | /* Set up registers for signal handler */ | ||
| 525 | regs->sp = (unsigned long) frame; | ||
| 526 | regs->ip = (unsigned long) ka->sa.sa_handler; | ||
| 527 | |||
| 528 | /* We use the x32 calling convention here... */ | ||
| 529 | regs->di = sig; | ||
| 530 | regs->si = (unsigned long) &frame->info; | ||
| 531 | regs->dx = (unsigned long) &frame->uc; | ||
| 532 | |||
| 533 | loadsegment(ds, __USER_DS); | ||
| 534 | loadsegment(es, __USER_DS); | ||
| 535 | |||
| 536 | regs->cs = __USER_CS; | ||
| 537 | regs->ss = __USER_DS; | ||
| 538 | #endif /* CONFIG_X86_X32_ABI */ | ||
| 539 | |||
| 540 | return 0; | ||
| 541 | } | ||
| 542 | |||
| 477 | #ifdef CONFIG_X86_32 | 543 | #ifdef CONFIG_X86_32 |
| 478 | /* | 544 | /* |
| 479 | * Atomically swap in the new signal mask, and wait for a signal. | 545 | * Atomically swap in the new signal mask, and wait for a signal. |
| @@ -612,55 +678,22 @@ static int signr_convert(int sig) | |||
| 612 | return sig; | 678 | return sig; |
| 613 | } | 679 | } |
| 614 | 680 | ||
| 615 | #ifdef CONFIG_X86_32 | ||
| 616 | |||
| 617 | #define is_ia32 1 | ||
| 618 | #define ia32_setup_frame __setup_frame | ||
| 619 | #define ia32_setup_rt_frame __setup_rt_frame | ||
| 620 | |||
| 621 | #else /* !CONFIG_X86_32 */ | ||
| 622 | |||
| 623 | #ifdef CONFIG_IA32_EMULATION | ||
| 624 | #define is_ia32 test_thread_flag(TIF_IA32) | ||
| 625 | #else /* !CONFIG_IA32_EMULATION */ | ||
| 626 | #define is_ia32 0 | ||
| 627 | #endif /* CONFIG_IA32_EMULATION */ | ||
| 628 | |||
| 629 | #ifdef CONFIG_X86_X32_ABI | ||
| 630 | #define is_x32 test_thread_flag(TIF_X32) | ||
| 631 | |||
| 632 | static int x32_setup_rt_frame(int sig, struct k_sigaction *ka, | ||
| 633 | siginfo_t *info, compat_sigset_t *set, | ||
| 634 | struct pt_regs *regs); | ||
| 635 | #else /* !CONFIG_X86_X32_ABI */ | ||
| 636 | #define is_x32 0 | ||
| 637 | #endif /* CONFIG_X86_X32_ABI */ | ||
| 638 | |||
| 639 | int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | ||
| 640 | sigset_t *set, struct pt_regs *regs); | ||
| 641 | int ia32_setup_frame(int sig, struct k_sigaction *ka, | ||
| 642 | sigset_t *set, struct pt_regs *regs); | ||
| 643 | |||
| 644 | #endif /* CONFIG_X86_32 */ | ||
| 645 | |||
| 646 | static int | 681 | static int |
| 647 | setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | 682 | setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, |
| 648 | struct pt_regs *regs) | 683 | struct pt_regs *regs) |
| 649 | { | 684 | { |
| 650 | int usig = signr_convert(sig); | 685 | int usig = signr_convert(sig); |
| 651 | sigset_t *set = sigmask_to_save(); | 686 | sigset_t *set = sigmask_to_save(); |
| 687 | compat_sigset_t *cset = (compat_sigset_t *) set; | ||
| 652 | 688 | ||
| 653 | /* Set up the stack frame */ | 689 | /* Set up the stack frame */ |
| 654 | if (is_ia32) { | 690 | if (is_ia32_frame()) { |
| 655 | if (ka->sa.sa_flags & SA_SIGINFO) | 691 | if (ka->sa.sa_flags & SA_SIGINFO) |
| 656 | return ia32_setup_rt_frame(usig, ka, info, set, regs); | 692 | return ia32_setup_rt_frame(usig, ka, info, cset, regs); |
| 657 | else | 693 | else |
| 658 | return ia32_setup_frame(usig, ka, set, regs); | 694 | return ia32_setup_frame(usig, ka, cset, regs); |
| 659 | #ifdef CONFIG_X86_X32_ABI | 695 | } else if (is_x32_frame()) { |
| 660 | } else if (is_x32) { | 696 | return x32_setup_rt_frame(usig, ka, info, cset, regs); |
| 661 | return x32_setup_rt_frame(usig, ka, info, | ||
| 662 | (compat_sigset_t *)set, regs); | ||
| 663 | #endif | ||
| 664 | } else { | 697 | } else { |
| 665 | return __setup_rt_frame(sig, ka, info, set, regs); | 698 | return __setup_rt_frame(sig, ka, info, set, regs); |
| 666 | } | 699 | } |
| @@ -779,6 +812,8 @@ static void do_signal(struct pt_regs *regs) | |||
| 779 | void | 812 | void |
| 780 | do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags) | 813 | do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags) |
| 781 | { | 814 | { |
| 815 | rcu_user_exit(); | ||
| 816 | |||
| 782 | #ifdef CONFIG_X86_MCE | 817 | #ifdef CONFIG_X86_MCE |
| 783 | /* notify userspace of pending MCEs */ | 818 | /* notify userspace of pending MCEs */ |
| 784 | if (thread_info_flags & _TIF_MCE_NOTIFY) | 819 | if (thread_info_flags & _TIF_MCE_NOTIFY) |
| @@ -804,6 +839,8 @@ do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags) | |||
| 804 | #ifdef CONFIG_X86_32 | 839 | #ifdef CONFIG_X86_32 |
| 805 | clear_thread_flag(TIF_IRET); | 840 | clear_thread_flag(TIF_IRET); |
| 806 | #endif /* CONFIG_X86_32 */ | 841 | #endif /* CONFIG_X86_32 */ |
| 842 | |||
| 843 | rcu_user_enter(); | ||
| 807 | } | 844 | } |
| 808 | 845 | ||
| 809 | void signal_fault(struct pt_regs *regs, void __user *frame, char *where) | 846 | void signal_fault(struct pt_regs *regs, void __user *frame, char *where) |
| @@ -824,72 +861,6 @@ void signal_fault(struct pt_regs *regs, void __user *frame, char *where) | |||
| 824 | } | 861 | } |
| 825 | 862 | ||
| 826 | #ifdef CONFIG_X86_X32_ABI | 863 | #ifdef CONFIG_X86_X32_ABI |
| 827 | static int x32_setup_rt_frame(int sig, struct k_sigaction *ka, | ||
| 828 | siginfo_t *info, compat_sigset_t *set, | ||
| 829 | struct pt_regs *regs) | ||
| 830 | { | ||
| 831 | struct rt_sigframe_x32 __user *frame; | ||
| 832 | void __user *restorer; | ||
| 833 | int err = 0; | ||
| 834 | void __user *fpstate = NULL; | ||
| 835 | |||
| 836 | frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate); | ||
| 837 | |||
| 838 | if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) | ||
| 839 | return -EFAULT; | ||
| 840 | |||
| 841 | if (ka->sa.sa_flags & SA_SIGINFO) { | ||
| 842 | if (copy_siginfo_to_user32(&frame->info, info)) | ||
| 843 | return -EFAULT; | ||
| 844 | } | ||
| 845 | |||
| 846 | put_user_try { | ||
| 847 | /* Create the ucontext. */ | ||
| 848 | if (cpu_has_xsave) | ||
| 849 | put_user_ex(UC_FP_XSTATE, &frame->uc.uc_flags); | ||
| 850 | else | ||
| 851 | put_user_ex(0, &frame->uc.uc_flags); | ||
| 852 | put_user_ex(0, &frame->uc.uc_link); | ||
| 853 | put_user_ex(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); | ||
| 854 | put_user_ex(sas_ss_flags(regs->sp), | ||
| 855 | &frame->uc.uc_stack.ss_flags); | ||
| 856 | put_user_ex(current->sas_ss_size, &frame->uc.uc_stack.ss_size); | ||
| 857 | put_user_ex(0, &frame->uc.uc__pad0); | ||
| 858 | err |= setup_sigcontext(&frame->uc.uc_mcontext, fpstate, | ||
| 859 | regs, set->sig[0]); | ||
| 860 | err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); | ||
| 861 | |||
| 862 | if (ka->sa.sa_flags & SA_RESTORER) { | ||
| 863 | restorer = ka->sa.sa_restorer; | ||
| 864 | } else { | ||
| 865 | /* could use a vstub here */ | ||
| 866 | restorer = NULL; | ||
| 867 | err |= -EFAULT; | ||
| 868 | } | ||
| 869 | put_user_ex(restorer, &frame->pretcode); | ||
| 870 | } put_user_catch(err); | ||
| 871 | |||
| 872 | if (err) | ||
| 873 | return -EFAULT; | ||
| 874 | |||
| 875 | /* Set up registers for signal handler */ | ||
| 876 | regs->sp = (unsigned long) frame; | ||
| 877 | regs->ip = (unsigned long) ka->sa.sa_handler; | ||
| 878 | |||
| 879 | /* We use the x32 calling convention here... */ | ||
| 880 | regs->di = sig; | ||
| 881 | regs->si = (unsigned long) &frame->info; | ||
| 882 | regs->dx = (unsigned long) &frame->uc; | ||
| 883 | |||
| 884 | loadsegment(ds, __USER_DS); | ||
| 885 | loadsegment(es, __USER_DS); | ||
| 886 | |||
| 887 | regs->cs = __USER_CS; | ||
| 888 | regs->ss = __USER_DS; | ||
| 889 | |||
| 890 | return 0; | ||
| 891 | } | ||
| 892 | |||
| 893 | asmlinkage long sys32_x32_rt_sigreturn(struct pt_regs *regs) | 864 | asmlinkage long sys32_x32_rt_sigreturn(struct pt_regs *regs) |
| 894 | { | 865 | { |
| 895 | struct rt_sigframe_x32 __user *frame; | 866 | struct rt_sigframe_x32 __user *frame; |
