diff options
| -rw-r--r-- | arch/x86/kernel/entry_64.S | 54 |
1 files changed, 54 insertions, 0 deletions
diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index 501212f14c87..eeab4cf8b2c9 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S | |||
| @@ -794,6 +794,60 @@ retint_swapgs: /* return to user-space */ | |||
| 794 | */ | 794 | */ |
| 795 | DISABLE_INTERRUPTS(CLBR_ANY) | 795 | DISABLE_INTERRUPTS(CLBR_ANY) |
| 796 | TRACE_IRQS_IRETQ | 796 | TRACE_IRQS_IRETQ |
| 797 | |||
| 798 | /* | ||
| 799 | * Try to use SYSRET instead of IRET if we're returning to | ||
| 800 | * a completely clean 64-bit userspace context. | ||
| 801 | */ | ||
| 802 | movq (RCX-R11)(%rsp), %rcx | ||
| 803 | cmpq %rcx,(RIP-R11)(%rsp) /* RCX == RIP */ | ||
| 804 | jne opportunistic_sysret_failed | ||
| 805 | |||
| 806 | /* | ||
| 807 | * On Intel CPUs, sysret with non-canonical RCX/RIP will #GP | ||
| 808 | * in kernel space. This essentially lets the user take over | ||
| 809 | * the kernel, since userspace controls RSP. It's not worth | ||
| 810 | * testing for canonicalness exactly -- this check detects any | ||
| 811 | * of the 17 high bits set, which is true for non-canonical | ||
| 812 | * or kernel addresses. (This will pessimize vsyscall=native. | ||
| 813 | * Big deal.) | ||
| 814 | * | ||
| 815 | * If virtual addresses ever become wider, this will need | ||
| 816 | * to be updated to remain correct on both old and new CPUs. | ||
| 817 | */ | ||
| 818 | .ifne __VIRTUAL_MASK_SHIFT - 47 | ||
| 819 | .error "virtual address width changed -- sysret checks need update" | ||
| 820 | .endif | ||
| 821 | shr $__VIRTUAL_MASK_SHIFT, %rcx | ||
| 822 | jnz opportunistic_sysret_failed | ||
| 823 | |||
| 824 | cmpq $__USER_CS,(CS-R11)(%rsp) /* CS must match SYSRET */ | ||
| 825 | jne opportunistic_sysret_failed | ||
| 826 | |||
| 827 | movq (R11-ARGOFFSET)(%rsp), %r11 | ||
| 828 | cmpq %r11,(EFLAGS-ARGOFFSET)(%rsp) /* R11 == RFLAGS */ | ||
| 829 | jne opportunistic_sysret_failed | ||
| 830 | |||
| 831 | testq $X86_EFLAGS_RF,%r11 /* sysret can't restore RF */ | ||
| 832 | jnz opportunistic_sysret_failed | ||
| 833 | |||
| 834 | /* nothing to check for RSP */ | ||
| 835 | |||
| 836 | cmpq $__USER_DS,(SS-ARGOFFSET)(%rsp) /* SS must match SYSRET */ | ||
| 837 | jne opportunistic_sysret_failed | ||
| 838 | |||
| 839 | /* | ||
| 840 | * We win! This label is here just for ease of understanding | ||
| 841 | * perf profiles. Nothing jumps here. | ||
| 842 | */ | ||
| 843 | irq_return_via_sysret: | ||
| 844 | CFI_REMEMBER_STATE | ||
| 845 | RESTORE_ARGS 1,8,1 | ||
| 846 | movq (RSP-RIP)(%rsp),%rsp | ||
| 847 | USERGS_SYSRET64 | ||
| 848 | CFI_RESTORE_STATE | ||
| 849 | |||
| 850 | opportunistic_sysret_failed: | ||
| 797 | SWAPGS | 851 | SWAPGS |
| 798 | jmp restore_args | 852 | jmp restore_args |
| 799 | 853 | ||
