2022-04-09 13:21:29

by Peter Zijlstra

[permalink] [raw]
Subject: [RFC][PATCH] x86: PUSH_AND_CLEAR_REGS_COMPAT


How insane?

Signed-off-by: Peter Zijlstra (Intel) <[email protected]>
---
arch/x86/entry/calling.h | 25 ++++++++----
arch/x86/entry/entry_64_compat.S | 87 ++--------------------------------------
2 files changed, 21 insertions(+), 91 deletions(-)

diff --git a/arch/x86/entry/calling.h b/arch/x86/entry/calling.h
index a4c061fb7c6e..7f938704da59 100644
--- a/arch/x86/entry/calling.h
+++ b/arch/x86/entry/calling.h
@@ -63,13 +63,15 @@ For 32-bit we have the following conventions - kernel is built with
* for assembly code:
*/

-.macro PUSH_REGS rdx=%rdx rax=%rax save_ret=0
+.macro PUSH_REGS rdx=%rdx rax=%rax save_ret=0 save_rdi=1
.if \save_ret
pushq %rsi /* pt_regs->si */
movq 8(%rsp), %rsi /* temporarily store the return address in %rsi */
movq %rdi, 8(%rsp) /* pt_regs->di (overwriting original return address) */
.else
+ .if \save_rdi
pushq %rdi /* pt_regs->di */
+ .endif
pushq %rsi /* pt_regs->si */
.endif
pushq \rdx /* pt_regs->dx */
@@ -92,7 +94,7 @@ For 32-bit we have the following conventions - kernel is built with
.endif
.endm

-.macro CLEAR_REGS
+.macro CLEAR_REGS_lo
/*
* Sanitize registers of values that a speculation attack might
* otherwise want to exploit. The lower registers are likely clobbered
@@ -101,22 +103,31 @@ For 32-bit we have the following conventions - kernel is built with
*/
xorl %edx, %edx /* nospec dx */
xorl %ecx, %ecx /* nospec cx */
+ xorl %ebx, %ebx /* nospec rbx */
+ xorl %ebp, %ebp /* nospec rbp */
+.endm
+
+.macro CLEAR_REGS_hi
xorl %r8d, %r8d /* nospec r8 */
xorl %r9d, %r9d /* nospec r9 */
xorl %r10d, %r10d /* nospec r10 */
xorl %r11d, %r11d /* nospec r11 */
- xorl %ebx, %ebx /* nospec rbx */
- xorl %ebp, %ebp /* nospec rbp */
xorl %r12d, %r12d /* nospec r12 */
xorl %r13d, %r13d /* nospec r13 */
xorl %r14d, %r14d /* nospec r14 */
xorl %r15d, %r15d /* nospec r15 */
+.endm

+.macro PUSH_AND_CLEAR_REGS rdx=%rdx rax=%rax save_ret=0 save_rdi=1
+ PUSH_REGS rdx=\rdx, rax=\rax, save_ret=\save_ret save_rdi=\save_rdi
+ CLEAR_REGS_lo
+ CLEAR_REGS_hi
.endm

-.macro PUSH_AND_CLEAR_REGS rdx=%rdx rax=%rax save_ret=0
- PUSH_REGS rdx=\rdx, rax=\rax, save_ret=\save_ret
- CLEAR_REGS
+.macro PUSH_AND_CLEAR_REGS_COMPAT rdx=%rdx rax=%rax save_ret=0 save_rdi=1
+ CLEAR_REGS_hi
+ PUSH_REGS rdx=\rdx, rax=\rax, save_ret=\save_ret save_rdi=\save_rdi
+ CLEAR_REGS_lo
.endm

.macro POP_REGS pop_rdi=1 skip_r11rcx=0
diff --git a/arch/x86/entry/entry_64_compat.S b/arch/x86/entry/entry_64_compat.S
index 4fdb007cddbd..44a3eaf15de6 100644
--- a/arch/x86/entry/entry_64_compat.S
+++ b/arch/x86/entry/entry_64_compat.S
@@ -83,32 +83,7 @@ SYM_INNER_LABEL(entry_SYSENTER_compat_after_hwframe, SYM_L_GLOBAL)
movl %eax, %eax

pushq %rax /* pt_regs->orig_ax */
- pushq %rdi /* pt_regs->di */
- pushq %rsi /* pt_regs->si */
- pushq %rdx /* pt_regs->dx */
- pushq %rcx /* pt_regs->cx */
- pushq $-ENOSYS /* pt_regs->ax */
- pushq $0 /* pt_regs->r8 = 0 */
- xorl %r8d, %r8d /* nospec r8 */
- pushq $0 /* pt_regs->r9 = 0 */
- xorl %r9d, %r9d /* nospec r9 */
- pushq $0 /* pt_regs->r10 = 0 */
- xorl %r10d, %r10d /* nospec r10 */
- pushq $0 /* pt_regs->r11 = 0 */
- xorl %r11d, %r11d /* nospec r11 */
- pushq %rbx /* pt_regs->rbx */
- xorl %ebx, %ebx /* nospec rbx */
- pushq %rbp /* pt_regs->rbp (will be overwritten) */
- xorl %ebp, %ebp /* nospec rbp */
- pushq $0 /* pt_regs->r12 = 0 */
- xorl %r12d, %r12d /* nospec r12 */
- pushq $0 /* pt_regs->r13 = 0 */
- xorl %r13d, %r13d /* nospec r13 */
- pushq $0 /* pt_regs->r14 = 0 */
- xorl %r14d, %r14d /* nospec r14 */
- pushq $0 /* pt_regs->r15 = 0 */
- xorl %r15d, %r15d /* nospec r15 */
-
+ PUSH_AND_CLEAR_REGS_COMPAT rax=$-ENOSYS
UNWIND_HINT_REGS

cld
@@ -225,35 +200,7 @@ SYM_INNER_LABEL(entry_SYSCALL_compat_safe_stack, SYM_L_GLOBAL)
SYM_INNER_LABEL(entry_SYSCALL_compat_after_hwframe, SYM_L_GLOBAL)
movl %eax, %eax /* discard orig_ax high bits */
pushq %rax /* pt_regs->orig_ax */
- pushq %rdi /* pt_regs->di */
- pushq %rsi /* pt_regs->si */
- xorl %esi, %esi /* nospec si */
- pushq %rdx /* pt_regs->dx */
- xorl %edx, %edx /* nospec dx */
- pushq %rbp /* pt_regs->cx (stashed in bp) */
- xorl %ecx, %ecx /* nospec cx */
- pushq $-ENOSYS /* pt_regs->ax */
- pushq $0 /* pt_regs->r8 = 0 */
- xorl %r8d, %r8d /* nospec r8 */
- pushq $0 /* pt_regs->r9 = 0 */
- xorl %r9d, %r9d /* nospec r9 */
- pushq $0 /* pt_regs->r10 = 0 */
- xorl %r10d, %r10d /* nospec r10 */
- pushq $0 /* pt_regs->r11 = 0 */
- xorl %r11d, %r11d /* nospec r11 */
- pushq %rbx /* pt_regs->rbx */
- xorl %ebx, %ebx /* nospec rbx */
- pushq %rbp /* pt_regs->rbp (will be overwritten) */
- xorl %ebp, %ebp /* nospec rbp */
- pushq $0 /* pt_regs->r12 = 0 */
- xorl %r12d, %r12d /* nospec r12 */
- pushq $0 /* pt_regs->r13 = 0 */
- xorl %r13d, %r13d /* nospec r13 */
- pushq $0 /* pt_regs->r14 = 0 */
- xorl %r14d, %r14d /* nospec r14 */
- pushq $0 /* pt_regs->r15 = 0 */
- xorl %r15d, %r15d /* nospec r15 */
-
+ PUSH_AND_CLEAR_REGS_COMPAT rax=$-ENOSYS
UNWIND_HINT_REGS

movq %rsp, %rdi
@@ -381,35 +328,7 @@ SYM_CODE_START(entry_INT80_compat)
pushq 1*8(%rdi) /* regs->orig_ax */
pushq (%rdi) /* pt_regs->di */
.Lint80_keep_stack:
-
- pushq %rsi /* pt_regs->si */
- xorl %esi, %esi /* nospec si */
- pushq %rdx /* pt_regs->dx */
- xorl %edx, %edx /* nospec dx */
- pushq %rcx /* pt_regs->cx */
- xorl %ecx, %ecx /* nospec cx */
- pushq $-ENOSYS /* pt_regs->ax */
- pushq %r8 /* pt_regs->r8 */
- xorl %r8d, %r8d /* nospec r8 */
- pushq %r9 /* pt_regs->r9 */
- xorl %r9d, %r9d /* nospec r9 */
- pushq %r10 /* pt_regs->r10*/
- xorl %r10d, %r10d /* nospec r10 */
- pushq %r11 /* pt_regs->r11 */
- xorl %r11d, %r11d /* nospec r11 */
- pushq %rbx /* pt_regs->rbx */
- xorl %ebx, %ebx /* nospec rbx */
- pushq %rbp /* pt_regs->rbp */
- xorl %ebp, %ebp /* nospec rbp */
- pushq %r12 /* pt_regs->r12 */
- xorl %r12d, %r12d /* nospec r12 */
- pushq %r13 /* pt_regs->r13 */
- xorl %r13d, %r13d /* nospec r13 */
- pushq %r14 /* pt_regs->r14 */
- xorl %r14d, %r14d /* nospec r14 */
- pushq %r15 /* pt_regs->r15 */
- xorl %r15d, %r15d /* nospec r15 */
-
+ PUSH_AND_CLEAR_REGS save_rdi=0
UNWIND_HINT_REGS

cld


2022-04-09 13:24:05

by Josh Poimboeuf

[permalink] [raw]
Subject: Re: [RFC][PATCH] x86: PUSH_AND_CLEAR_REGS_COMPAT

On Sat, Apr 09, 2022 at 01:14:47AM +0200, Peter Zijlstra wrote:
> On Sat, Apr 09, 2022 at 12:38:27AM +0200, Peter Zijlstra wrote:
> >
> > How insane?
>
> Anyway, the questino is; since int80 doesn't wipe the high regs, can we
> get away with the SYS*_compat things not doing that either and then all
> using the normal PUSH_AND_CLEAR_REGS without having to invent _COMPAT
> for that?

I'd rather not, clearing the register values on the stack is a good
thing as it gives attackers less control. In fact I wish we could do
that for the 64-bit syscalls, but alas, callee-saved registers and all.

--
Josh

2022-04-11 15:07:14

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [RFC][PATCH] x86: PUSH_AND_CLEAR_REGS_COMPAT

On Sat, Apr 09, 2022 at 12:38:27AM +0200, Peter Zijlstra wrote:
>
> How insane?

Anyway, the questino is; since int80 doesn't wipe the high regs, can we
get away with the SYS*_compat things not doing that either and then all
using the normal PUSH_AND_CLEAR_REGS without having to invent _COMPAT
for that?

2022-04-11 15:42:06

by Brian Gerst

[permalink] [raw]
Subject: Re: [RFC][PATCH] x86: PUSH_AND_CLEAR_REGS_COMPAT

On Sat, Apr 9, 2022 at 1:44 PM Peter Zijlstra <[email protected]> wrote:
>
> On Sat, Apr 09, 2022 at 12:38:27AM +0200, Peter Zijlstra wrote:
> >
> > How insane?
>
> Anyway, the questino is; since int80 doesn't wipe the high regs, can we
> get away with the SYS*_compat things not doing that either and then all
> using the normal PUSH_AND_CLEAR_REGS without having to invent _COMPAT
> for that?

For a pure 32-bit process it doesn't matter whether the upper
registers are preserved or not, since they are inaccessible.
Mixed-mode code may assume the upper registers are preserved across a
call to 32-bit code, although it would be very unlikely to encounter
SYSENTER or SYSCALL instructions anywhere but the Linux VDSO (and use
anywhere else would cause undesirable results).

There is no compelling reason to not preserve them, and code
simplification is a good benefit.

--
Brian Gerst