Skip to content

Commit ca282b4

Browse files
Brian GerstIngo Molnar
authored andcommitted
x86/entry/64: Convert SYSRET validation tests to C
No change in functionality expected. Signed-off-by: Brian Gerst <[email protected]> Signed-off-by: Ingo Molnar <[email protected]> Cc: Andy Lutomirski <[email protected]> Cc: Borislav Petkov <[email protected]> Cc: Brian Gerst <[email protected]> Cc: Denys Vlasenko <[email protected]> Cc: H. Peter Anvin <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: Josh Poimboeuf <[email protected]> Cc: Uros Bizjak <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent bab9fa6 commit ca282b4

File tree

3 files changed

+45
-53
lines changed

3 files changed

+45
-53
lines changed

arch/x86/entry/common.c

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ static __always_inline bool do_syscall_x32(struct pt_regs *regs, int nr)
7171
return false;
7272
}
7373

74-
__visible noinstr void do_syscall_64(struct pt_regs *regs, int nr)
74+
/* Returns true to return using SYSRET, or false to use IRET */
75+
__visible noinstr bool do_syscall_64(struct pt_regs *regs, int nr)
7576
{
7677
add_random_kstack_offset();
7778
nr = syscall_enter_from_user_mode(regs, nr);
@@ -85,6 +86,46 @@ __visible noinstr void do_syscall_64(struct pt_regs *regs, int nr)
8586

8687
instrumentation_end();
8788
syscall_exit_to_user_mode(regs);
89+
90+
/*
91+
* Check that the register state is valid for using SYSRET to exit
92+
* to userspace. Otherwise use the slower but fully capable IRET
93+
* exit path.
94+
*/
95+
96+
/* XEN PV guests always use the IRET path */
97+
if (cpu_feature_enabled(X86_FEATURE_XENPV))
98+
return false;
99+
100+
/* SYSRET requires RCX == RIP and R11 == EFLAGS */
101+
if (unlikely(regs->cx != regs->ip || regs->r11 != regs->flags))
102+
return false;
103+
104+
/* CS and SS must match the values set in MSR_STAR */
105+
if (unlikely(regs->cs != __USER_CS || regs->ss != __USER_DS))
106+
return false;
107+
108+
/*
109+
* On Intel CPUs, SYSRET with non-canonical RCX/RIP will #GP
110+
* in kernel space. This essentially lets the user take over
111+
* the kernel, since userspace controls RSP.
112+
*
113+
* Change top bits to match the most significant bit (47th or 56th bit
114+
* depending on paging mode) in the address.
115+
*/
116+
if (unlikely(!__is_canonical_address(regs->ip, __VIRTUAL_MASK_SHIFT + 1)))
117+
return false;
118+
119+
/*
120+
* SYSRET cannot restore RF. It can restore TF, but unlike IRET,
121+
* restoring TF results in a trap from userspace immediately after
122+
* SYSRET.
123+
*/
124+
if (unlikely(regs->flags & (X86_EFLAGS_RF | X86_EFLAGS_TF)))
125+
return false;
126+
127+
/* Use SYSRET to exit to userspace */
128+
return true;
88129
}
89130
#endif
90131

arch/x86/entry/entry_64.S

Lines changed: 2 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -126,57 +126,8 @@ SYM_INNER_LABEL(entry_SYSCALL_64_after_hwframe, SYM_L_GLOBAL)
126126
* In the Xen PV case we must use iret anyway.
127127
*/
128128

129-
ALTERNATIVE "", "jmp swapgs_restore_regs_and_return_to_usermode", \
130-
X86_FEATURE_XENPV
131-
132-
movq RCX(%rsp), %rcx
133-
movq RIP(%rsp), %r11
134-
135-
cmpq %rcx, %r11 /* SYSRET requires RCX == RIP */
136-
jne swapgs_restore_regs_and_return_to_usermode
137-
138-
/*
139-
* On Intel CPUs, SYSRET with non-canonical RCX/RIP will #GP
140-
* in kernel space. This essentially lets the user take over
141-
* the kernel, since userspace controls RSP.
142-
*
143-
* If width of "canonical tail" ever becomes variable, this will need
144-
* to be updated to remain correct on both old and new CPUs.
145-
*
146-
* Change top bits to match most significant bit (47th or 56th bit
147-
* depending on paging mode) in the address.
148-
*/
149-
#ifdef CONFIG_X86_5LEVEL
150-
ALTERNATIVE "shl $(64 - 48), %rcx; sar $(64 - 48), %rcx", \
151-
"shl $(64 - 57), %rcx; sar $(64 - 57), %rcx", X86_FEATURE_LA57
152-
#else
153-
shl $(64 - (__VIRTUAL_MASK_SHIFT+1)), %rcx
154-
sar $(64 - (__VIRTUAL_MASK_SHIFT+1)), %rcx
155-
#endif
156-
157-
/* If this changed %rcx, it was not canonical */
158-
cmpq %rcx, %r11
159-
jne swapgs_restore_regs_and_return_to_usermode
160-
161-
cmpq $__USER_CS, CS(%rsp) /* CS must match SYSRET */
162-
jne swapgs_restore_regs_and_return_to_usermode
163-
164-
movq R11(%rsp), %r11
165-
cmpq %r11, EFLAGS(%rsp) /* R11 == RFLAGS */
166-
jne swapgs_restore_regs_and_return_to_usermode
167-
168-
/*
169-
* SYSRET cannot restore RF. It can restore TF, but unlike IRET,
170-
* restoring TF results in a trap from userspace immediately after
171-
* SYSRET.
172-
*/
173-
testq $(X86_EFLAGS_RF|X86_EFLAGS_TF), %r11
174-
jnz swapgs_restore_regs_and_return_to_usermode
175-
176-
/* nothing to check for RSP */
177-
178-
cmpq $__USER_DS, SS(%rsp) /* SS must match SYSRET */
179-
jne swapgs_restore_regs_and_return_to_usermode
129+
ALTERNATIVE "testb %al, %al; jz swapgs_restore_regs_and_return_to_usermode", \
130+
"jmp swapgs_restore_regs_and_return_to_usermode", X86_FEATURE_XENPV
180131

181132
/*
182133
* We win! This label is here just for ease of understanding

arch/x86/include/asm/syscall.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ static inline int syscall_get_arch(struct task_struct *task)
126126
? AUDIT_ARCH_I386 : AUDIT_ARCH_X86_64;
127127
}
128128

129-
void do_syscall_64(struct pt_regs *regs, int nr);
129+
bool do_syscall_64(struct pt_regs *regs, int nr);
130130

131131
#endif /* CONFIG_X86_32 */
132132

0 commit comments

Comments
 (0)