lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-Id: <20210913205446.10342-1-dvlasenk@redhat.com>
Date:   Mon, 13 Sep 2021 22:54:46 +0200
From:   Denys Vlasenko <dvlasenk@...hat.com>
To:     Andy Lutomirski <luto@...nel.org>,
        "H . Peter Anvin" <hpa@...or.com>
Cc:     Denys Vlasenko <dvlasenk@...hat.com>, x86@...nel.org,
        linux-kernel@...r.kernel.org
Subject: [PATCH] x86/entry: move ptregs->rsp and IF fixups to asm, remove do_SYSENTER_32()

This removes two loads, one store, and one level of function calls
from both 32- and 64-bit SYSENTER32 code paths:

do_SYSENTER_32() does two fixups:

    /* SYSENTER loses RSP, but the vDSO saved it in RBP. */
    regs->sp = regs->bp;
but we can just do this when we form regs->sp on the stack in the first place.

    /* SYSENTER clobbers EFLAGS.IF.  Assume it was set in usermode. */
    regs->flags |= X86_EFLAGS_IF;
but we already load regs->flags in asm code and peek at its bits in order
to clear TF et al. We can set IF and update regs->flags right there,
avoiding loading it again.

With this changed, do_SYSENTER_32() does not do anything except calling
do_fast_syscall_32(), so get rid of it.

While at it, tweak some outdated comments.

Signed-off-by: Denys Vlasenko <dvlasenk@...hat.com>
---
 arch/x86/entry/common.c          | 12 ------------
 arch/x86/entry/entry_32.S        | 13 +++++++++----
 arch/x86/entry/entry_64_compat.S | 19 ++++++++++++-------
 3 files changed, 21 insertions(+), 23 deletions(-)

diff --git a/arch/x86/entry/common.c b/arch/x86/entry/common.c
index 6c2826417b33..7639b0fc7e8a 100644
--- a/arch/x86/entry/common.c
+++ b/arch/x86/entry/common.c
@@ -233,18 +233,6 @@ __visible noinstr long do_fast_syscall_32(struct pt_regs *regs)
 		(regs->flags & (X86_EFLAGS_RF | X86_EFLAGS_TF | X86_EFLAGS_VM)) == 0;
 #endif
 }
-
-/* Returns 0 to return using IRET or 1 to return using SYSEXIT/SYSRETL. */
-__visible noinstr long do_SYSENTER_32(struct pt_regs *regs)
-{
-	/* SYSENTER loses RSP, but the vDSO saved it in RBP. */
-	regs->sp = regs->bp;
-
-	/* SYSENTER clobbers EFLAGS.IF.  Assume it was set in usermode. */
-	regs->flags |= X86_EFLAGS_IF;
-
-	return do_fast_syscall_32(regs);
-}
 #endif
 
 SYSCALL_DEFINE0(ni_syscall)
diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S
index ccb9d32768f3..e14b13e9c3e1 100644
--- a/arch/x86/entry/entry_32.S
+++ b/arch/x86/entry/entry_32.S
@@ -837,7 +837,8 @@ SYM_FUNC_START(entry_SYSENTER_32)
 
 .Lsysenter_past_esp:
 	pushl	$__USER_DS		/* pt_regs->ss */
-	pushl	$0			/* pt_regs->sp (placeholder) */
+	/* SYSENTER loses RSP, but the vDSO saved it in EBP. */
+	pushl	%ebp			/* pt_regs->sp */
 	pushfl				/* pt_regs->flags (except IF = 0) */
 	pushl	$__USER_CS		/* pt_regs->cs */
 	pushl	$0			/* pt_regs->ip = 0 (placeholder) */
@@ -845,7 +846,7 @@ SYM_FUNC_START(entry_SYSENTER_32)
 	SAVE_ALL pt_regs_ax=$-ENOSYS	/* save rest, stack already switched */
 
 	/*
-	 * SYSENTER doesn't filter flags, so we need to clear NT, AC
+	 * SYSENTER filters only IF and VM, so we need to clear NT, AC
 	 * and TF ourselves.  To save a few cycles, we can check whether
 	 * either was set instead of doing an unconditional popfq.
 	 * This needs to happen before enabling interrupts so that
@@ -863,12 +864,16 @@ SYM_FUNC_START(entry_SYSENTER_32)
 	 * we're keeping that code behind a branch which will predict as
 	 * not-taken and therefore its instructions won't be fetched.
 	 */
-	testl	$X86_EFLAGS_NT|X86_EFLAGS_AC|X86_EFLAGS_TF, PT_EFLAGS(%esp)
+	movl	PT_EFLAGS(%esp), %eax
+	testl	$X86_EFLAGS_NT|X86_EFLAGS_AC|X86_EFLAGS_TF, %eax
 	jnz	.Lsysenter_fix_flags
 .Lsysenter_flags_fixed:
+	/* SYSENTER cleared IF.  Assume it was set in usermode. */
+	orl	$X86_EFLAGS_IF, %eax
+	movl	%eax, PT_EFLAGS(%esp)
 
 	movl	%esp, %eax
-	call	do_SYSENTER_32
+	call	do_fast_syscall_32
 	testl	%eax, %eax
 	jz	.Lsyscall_32_done
 
diff --git a/arch/x86/entry/entry_64_compat.S b/arch/x86/entry/entry_64_compat.S
index 0051cf5c792d..71a9bc09b14d 100644
--- a/arch/x86/entry/entry_64_compat.S
+++ b/arch/x86/entry/entry_64_compat.S
@@ -59,13 +59,14 @@ SYM_CODE_START(entry_SYSENTER_compat)
 
 	/* Construct struct pt_regs on stack */
 	pushq	$__USER32_DS		/* pt_regs->ss */
-	pushq	$0			/* pt_regs->sp = 0 (placeholder) */
+	/* SYSENTER loses RSP, but the vDSO saved it in RBP. */
+	pushq	%rbp			/* pt_regs->sp */
 
 	/*
 	 * Push flags.  This is nasty.  First, interrupts are currently
-	 * off, but we need pt_regs->flags to have IF set.  Second, if TS
+	 * off, but we need pt_regs->flags to have IF set.  Second, if TF
 	 * was set in usermode, it's still set, and we're singlestepping
-	 * through this code.  do_SYSENTER_32() will fix up IF.
+	 * through this code.  We fix up IF before do_fast_syscall_32().
 	 */
 	pushfq				/* pt_regs->flags (except IF = 0) */
 	pushq	$__USER32_CS		/* pt_regs->cs */
@@ -113,7 +114,7 @@ SYM_INNER_LABEL(entry_SYSENTER_compat_after_hwframe, SYM_L_GLOBAL)
 	cld
 
 	/*
-	 * SYSENTER doesn't filter flags, so we need to clear NT and AC
+	 * SYSENTER filters only IF and VM, so we need to clear NT and AC
 	 * ourselves.  To save a few cycles, we can check whether
 	 * either was set instead of doing an unconditional popfq.
 	 * This needs to happen before enabling interrupts so that
@@ -131,12 +132,16 @@ SYM_INNER_LABEL(entry_SYSENTER_compat_after_hwframe, SYM_L_GLOBAL)
 	 * we're keeping that code behind a branch which will predict as
 	 * not-taken and therefore its instructions won't be fetched.
 	 */
-	testl	$X86_EFLAGS_NT|X86_EFLAGS_AC|X86_EFLAGS_TF, EFLAGS(%rsp)
+	movl	EFLAGS(%rsp), %edi
+	testl	$X86_EFLAGS_NT|X86_EFLAGS_AC|X86_EFLAGS_TF, %edi
 	jnz	.Lsysenter_fix_flags
 .Lsysenter_flags_fixed:
+	/* SYSENTER cleared IF.  Assume it was set in usermode. */
+	orl	$X86_EFLAGS_IF, %edi
+	movl	%edi, EFLAGS(%rsp)
 
 	movq	%rsp, %rdi
-	call	do_SYSENTER_32
+	call	do_fast_syscall_32
 	/* XEN PV guests always use IRET path */
 	ALTERNATIVE "testl %eax, %eax; jz swapgs_restore_regs_and_return_to_usermode", \
 		    "jmp swapgs_restore_regs_and_return_to_usermode", X86_FEATURE_XENPV
@@ -189,7 +194,7 @@ SYM_CODE_END(entry_SYSENTER_compat)
  * eax  system call number
  * ecx  return address
  * ebx  arg1
- * ebp  arg2	(note: not saved in the stack frame, should not be touched)
+ * ebp  arg2
  * edx  arg3
  * esi  arg4
  * edi  arg5
-- 
2.30.0

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ