aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/kernel/entry_64.S54
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 */
843irq_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
850opportunistic_sysret_failed:
797 SWAPGS 851 SWAPGS
798 jmp restore_args 852 jmp restore_args
799 853