Skip to content

Commit a1aab6f

Browse files
committed
Merge branch 'x86-asm-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 asm updates from Ingo Molnar: "Most of the changes relate to Peter Zijlstra's cleanup of ptregs handling, in particular the i386 part is now much simplified and standardized - no more partial ptregs stack frames via the esp/ss oddity. This simplifies ftrace, kprobes, the unwinder, ptrace, kdump and kgdb. There's also a CR4 hardening enhancements by Kees Cook, to make the generic platform functions such as native_write_cr4() less useful as ROP gadgets that disable SMEP/SMAP. Also protect the WP bit of CR0 against similar attacks. The rest is smaller cleanups/fixes" * 'x86-asm-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: x86/alternatives: Add int3_emulate_call() selftest x86/stackframe/32: Allow int3_emulate_push() x86/stackframe/32: Provide consistent pt_regs x86/stackframe, x86/ftrace: Add pt_regs frame annotations x86/stackframe, x86/kprobes: Fix frame pointer annotations x86/stackframe: Move ENCODE_FRAME_POINTER to asm/frame.h x86/entry/32: Clean up return from interrupt preemption path x86/asm: Pin sensitive CR0 bits x86/asm: Pin sensitive CR4 bits Documentation/x86: Fix path to entry_32.S x86/asm: Remove unused TASK_TI_flags from asm-offsets.c
2 parents dad1c12 + 7457c0d commit a1aab6f

26 files changed

+394
-265
lines changed

Documentation/x86/exception-tables.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ page fault handler::
3535
void do_page_fault(struct pt_regs *regs, unsigned long error_code)
3636

3737
in arch/x86/mm/fault.c. The parameters on the stack are set up by
38-
the low level assembly glue in arch/x86/kernel/entry_32.S. The parameter
38+
the low level assembly glue in arch/x86/entry/entry_32.S. The parameter
3939
regs is a pointer to the saved registers on the stack, error_code
4040
contains a reason code for the exception.
4141

arch/x86/entry/calling.h

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -172,21 +172,6 @@ For 32-bit we have the following conventions - kernel is built with
172172
.endif
173173
.endm
174174

175-
/*
176-
* This is a sneaky trick to help the unwinder find pt_regs on the stack. The
177-
* frame pointer is replaced with an encoded pointer to pt_regs. The encoding
178-
* is just setting the LSB, which makes it an invalid stack address and is also
179-
* a signal to the unwinder that it's a pt_regs pointer in disguise.
180-
*
181-
* NOTE: This macro must be used *after* PUSH_AND_CLEAR_REGS because it corrupts
182-
* the original rbp.
183-
*/
184-
.macro ENCODE_FRAME_POINTER ptregs_offset=0
185-
#ifdef CONFIG_FRAME_POINTER
186-
leaq 1+\ptregs_offset(%rsp), %rbp
187-
#endif
188-
.endm
189-
190175
#ifdef CONFIG_PAGE_TABLE_ISOLATION
191176

192177
/*

arch/x86/entry/entry_32.S

Lines changed: 105 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@
6767
# define preempt_stop(clobbers) DISABLE_INTERRUPTS(clobbers); TRACE_IRQS_OFF
6868
#else
6969
# define preempt_stop(clobbers)
70-
# define resume_kernel restore_all_kernel
7170
#endif
7271

7372
.macro TRACE_IRQS_IRET
@@ -203,9 +202,102 @@
203202
.Lend_\@:
204203
.endm
205204

205+
#define CS_FROM_ENTRY_STACK (1 << 31)
206+
#define CS_FROM_USER_CR3 (1 << 30)
207+
#define CS_FROM_KERNEL (1 << 29)
208+
209+
.macro FIXUP_FRAME
210+
/*
211+
* The high bits of the CS dword (__csh) are used for CS_FROM_*.
212+
* Clear them in case hardware didn't do this for us.
213+
*/
214+
andl $0x0000ffff, 3*4(%esp)
215+
216+
#ifdef CONFIG_VM86
217+
testl $X86_EFLAGS_VM, 4*4(%esp)
218+
jnz .Lfrom_usermode_no_fixup_\@
219+
#endif
220+
testl $SEGMENT_RPL_MASK, 3*4(%esp)
221+
jnz .Lfrom_usermode_no_fixup_\@
222+
223+
orl $CS_FROM_KERNEL, 3*4(%esp)
224+
225+
/*
226+
* When we're here from kernel mode; the (exception) stack looks like:
227+
*
228+
* 5*4(%esp) - <previous context>
229+
* 4*4(%esp) - flags
230+
* 3*4(%esp) - cs
231+
* 2*4(%esp) - ip
232+
* 1*4(%esp) - orig_eax
233+
* 0*4(%esp) - gs / function
234+
*
235+
* Lets build a 5 entry IRET frame after that, such that struct pt_regs
236+
* is complete and in particular regs->sp is correct. This gives us
237+
* the original 5 enties as gap:
238+
*
239+
* 12*4(%esp) - <previous context>
240+
* 11*4(%esp) - gap / flags
241+
* 10*4(%esp) - gap / cs
242+
* 9*4(%esp) - gap / ip
243+
* 8*4(%esp) - gap / orig_eax
244+
* 7*4(%esp) - gap / gs / function
245+
* 6*4(%esp) - ss
246+
* 5*4(%esp) - sp
247+
* 4*4(%esp) - flags
248+
* 3*4(%esp) - cs
249+
* 2*4(%esp) - ip
250+
* 1*4(%esp) - orig_eax
251+
* 0*4(%esp) - gs / function
252+
*/
253+
254+
pushl %ss # ss
255+
pushl %esp # sp (points at ss)
256+
addl $6*4, (%esp) # point sp back at the previous context
257+
pushl 6*4(%esp) # flags
258+
pushl 6*4(%esp) # cs
259+
pushl 6*4(%esp) # ip
260+
pushl 6*4(%esp) # orig_eax
261+
pushl 6*4(%esp) # gs / function
262+
.Lfrom_usermode_no_fixup_\@:
263+
.endm
264+
265+
.macro IRET_FRAME
266+
testl $CS_FROM_KERNEL, 1*4(%esp)
267+
jz .Lfinished_frame_\@
268+
269+
/*
270+
* Reconstruct the 3 entry IRET frame right after the (modified)
271+
* regs->sp without lowering %esp in between, such that an NMI in the
272+
* middle doesn't scribble our stack.
273+
*/
274+
pushl %eax
275+
pushl %ecx
276+
movl 5*4(%esp), %eax # (modified) regs->sp
277+
278+
movl 4*4(%esp), %ecx # flags
279+
movl %ecx, -4(%eax)
280+
281+
movl 3*4(%esp), %ecx # cs
282+
andl $0x0000ffff, %ecx
283+
movl %ecx, -8(%eax)
284+
285+
movl 2*4(%esp), %ecx # ip
286+
movl %ecx, -12(%eax)
287+
288+
movl 1*4(%esp), %ecx # eax
289+
movl %ecx, -16(%eax)
290+
291+
popl %ecx
292+
lea -16(%eax), %esp
293+
popl %eax
294+
.Lfinished_frame_\@:
295+
.endm
296+
206297
.macro SAVE_ALL pt_regs_ax=%eax switch_stacks=0
207298
cld
208299
PUSH_GS
300+
FIXUP_FRAME
209301
pushl %fs
210302
pushl %es
211303
pushl %ds
@@ -247,22 +339,6 @@
247339
.Lend_\@:
248340
.endm
249341

250-
/*
251-
* This is a sneaky trick to help the unwinder find pt_regs on the stack. The
252-
* frame pointer is replaced with an encoded pointer to pt_regs. The encoding
253-
* is just clearing the MSB, which makes it an invalid stack address and is also
254-
* a signal to the unwinder that it's a pt_regs pointer in disguise.
255-
*
256-
* NOTE: This macro must be used *after* SAVE_ALL because it corrupts the
257-
* original rbp.
258-
*/
259-
.macro ENCODE_FRAME_POINTER
260-
#ifdef CONFIG_FRAME_POINTER
261-
mov %esp, %ebp
262-
andl $0x7fffffff, %ebp
263-
#endif
264-
.endm
265-
266342
.macro RESTORE_INT_REGS
267343
popl %ebx
268344
popl %ecx
@@ -375,9 +451,6 @@
375451
* switch to it before we do any copying.
376452
*/
377453

378-
#define CS_FROM_ENTRY_STACK (1 << 31)
379-
#define CS_FROM_USER_CR3 (1 << 30)
380-
381454
.macro SWITCH_TO_KERNEL_STACK
382455

383456
ALTERNATIVE "", "jmp .Lend_\@", X86_FEATURE_XENPV
@@ -391,13 +464,6 @@
391464
* that register for the time this macro runs
392465
*/
393466

394-
/*
395-
* The high bits of the CS dword (__csh) are used for
396-
* CS_FROM_ENTRY_STACK and CS_FROM_USER_CR3. Clear them in case
397-
* hardware didn't do this for us.
398-
*/
399-
andl $(0x0000ffff), PT_CS(%esp)
400-
401467
/* Are we on the entry stack? Bail out if not! */
402468
movl PER_CPU_VAR(cpu_entry_area), %ecx
403469
addl $CPU_ENTRY_AREA_entry_stack + SIZEOF_entry_stack, %ecx
@@ -755,7 +821,7 @@ ret_from_intr:
755821
andl $SEGMENT_RPL_MASK, %eax
756822
#endif
757823
cmpl $USER_RPL, %eax
758-
jb resume_kernel # not returning to v8086 or userspace
824+
jb restore_all_kernel # not returning to v8086 or userspace
759825

760826
ENTRY(resume_userspace)
761827
DISABLE_INTERRUPTS(CLBR_ANY)
@@ -765,18 +831,6 @@ ENTRY(resume_userspace)
765831
jmp restore_all
766832
END(ret_from_exception)
767833

768-
#ifdef CONFIG_PREEMPT
769-
ENTRY(resume_kernel)
770-
DISABLE_INTERRUPTS(CLBR_ANY)
771-
cmpl $0, PER_CPU_VAR(__preempt_count)
772-
jnz restore_all_kernel
773-
testl $X86_EFLAGS_IF, PT_EFLAGS(%esp) # interrupts off (exception path) ?
774-
jz restore_all_kernel
775-
call preempt_schedule_irq
776-
jmp restore_all_kernel
777-
END(resume_kernel)
778-
#endif
779-
780834
GLOBAL(__begin_SYSENTER_singlestep_region)
781835
/*
782836
* All code from here through __end_SYSENTER_singlestep_region is subject
@@ -1019,6 +1073,7 @@ restore_all:
10191073
/* Restore user state */
10201074
RESTORE_REGS pop=4 # skip orig_eax/error_code
10211075
.Lirq_return:
1076+
IRET_FRAME
10221077
/*
10231078
* ARCH_HAS_MEMBARRIER_SYNC_CORE rely on IRET core serialization
10241079
* when returning from IPI handler and when returning from
@@ -1027,6 +1082,15 @@ restore_all:
10271082
INTERRUPT_RETURN
10281083

10291084
restore_all_kernel:
1085+
#ifdef CONFIG_PREEMPT
1086+
DISABLE_INTERRUPTS(CLBR_ANY)
1087+
cmpl $0, PER_CPU_VAR(__preempt_count)
1088+
jnz .Lno_preempt
1089+
testl $X86_EFLAGS_IF, PT_EFLAGS(%esp) # interrupts off (exception path) ?
1090+
jz .Lno_preempt
1091+
call preempt_schedule_irq
1092+
.Lno_preempt:
1093+
#endif
10301094
TRACE_IRQS_IRET
10311095
PARANOID_EXIT_TO_KERNEL_MODE
10321096
BUG_IF_WRONG_CR3
@@ -1384,6 +1448,7 @@ END(page_fault)
13841448

13851449
common_exception:
13861450
/* the function address is in %gs's slot on the stack */
1451+
FIXUP_FRAME
13871452
pushl %fs
13881453
pushl %es
13891454
pushl %ds

arch/x86/include/asm/frame.h

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,35 @@
2222
pop %_ASM_BP
2323
.endm
2424

25+
#ifdef CONFIG_X86_64
26+
/*
27+
* This is a sneaky trick to help the unwinder find pt_regs on the stack. The
28+
* frame pointer is replaced with an encoded pointer to pt_regs. The encoding
29+
* is just setting the LSB, which makes it an invalid stack address and is also
30+
* a signal to the unwinder that it's a pt_regs pointer in disguise.
31+
*
32+
* NOTE: This macro must be used *after* PUSH_AND_CLEAR_REGS because it corrupts
33+
* the original rbp.
34+
*/
35+
.macro ENCODE_FRAME_POINTER ptregs_offset=0
36+
leaq 1+\ptregs_offset(%rsp), %rbp
37+
.endm
38+
#else /* !CONFIG_X86_64 */
39+
/*
40+
* This is a sneaky trick to help the unwinder find pt_regs on the stack. The
41+
* frame pointer is replaced with an encoded pointer to pt_regs. The encoding
42+
* is just clearing the MSB, which makes it an invalid stack address and is also
43+
* a signal to the unwinder that it's a pt_regs pointer in disguise.
44+
*
45+
* NOTE: This macro must be used *after* SAVE_ALL because it corrupts the
46+
* original ebp.
47+
*/
48+
.macro ENCODE_FRAME_POINTER
49+
mov %esp, %ebp
50+
andl $0x7fffffff, %ebp
51+
.endm
52+
#endif /* CONFIG_X86_64 */
53+
2554
#else /* !__ASSEMBLY__ */
2655

2756
#define FRAME_BEGIN \
@@ -30,12 +59,32 @@
3059

3160
#define FRAME_END "pop %" _ASM_BP "\n"
3261

62+
#ifdef CONFIG_X86_64
63+
#define ENCODE_FRAME_POINTER \
64+
"lea 1(%rsp), %rbp\n\t"
65+
#else /* !CONFIG_X86_64 */
66+
#define ENCODE_FRAME_POINTER \
67+
"movl %esp, %ebp\n\t" \
68+
"andl $0x7fffffff, %ebp\n\t"
69+
#endif /* CONFIG_X86_64 */
70+
3371
#endif /* __ASSEMBLY__ */
3472

3573
#define FRAME_OFFSET __ASM_SEL(4, 8)
3674

3775
#else /* !CONFIG_FRAME_POINTER */
3876

77+
#ifdef __ASSEMBLY__
78+
79+
.macro ENCODE_FRAME_POINTER ptregs_offset=0
80+
.endm
81+
82+
#else /* !__ASSEMBLY */
83+
84+
#define ENCODE_FRAME_POINTER
85+
86+
#endif
87+
3988
#define FRAME_BEGIN
4089
#define FRAME_END
4190
#define FRAME_OFFSET 0

arch/x86/include/asm/kexec.h

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -70,22 +70,6 @@ struct kimage;
7070
#define KEXEC_BACKUP_SRC_START (0UL)
7171
#define KEXEC_BACKUP_SRC_END (640 * 1024UL - 1) /* 640K */
7272

73-
/*
74-
* CPU does not save ss and sp on stack if execution is already
75-
* running in kernel mode at the time of NMI occurrence. This code
76-
* fixes it.
77-
*/
78-
static inline void crash_fixup_ss_esp(struct pt_regs *newregs,
79-
struct pt_regs *oldregs)
80-
{
81-
#ifdef CONFIG_X86_32
82-
newregs->sp = (unsigned long)&(oldregs->sp);
83-
asm volatile("xorl %%eax, %%eax\n\t"
84-
"movw %%ss, %%ax\n\t"
85-
:"=a"(newregs->ss));
86-
#endif
87-
}
88-
8973
/*
9074
* This function is responsible for capturing register states if coming
9175
* via panic otherwise just fix up the ss and sp if coming via kernel
@@ -96,7 +80,6 @@ static inline void crash_setup_regs(struct pt_regs *newregs,
9680
{
9781
if (oldregs) {
9882
memcpy(newregs, oldregs, sizeof(*newregs));
99-
crash_fixup_ss_esp(newregs, oldregs);
10083
} else {
10184
#ifdef CONFIG_X86_32
10285
asm volatile("movl %%ebx,%0" : "=m"(newregs->bx));

arch/x86/include/asm/ptrace.h

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -166,14 +166,10 @@ static inline bool user_64bit_mode(struct pt_regs *regs)
166166
#define compat_user_stack_pointer() current_pt_regs()->sp
167167
#endif
168168

169-
#ifdef CONFIG_X86_32
170-
extern unsigned long kernel_stack_pointer(struct pt_regs *regs);
171-
#else
172169
static inline unsigned long kernel_stack_pointer(struct pt_regs *regs)
173170
{
174171
return regs->sp;
175172
}
176-
#endif
177173

178174
#define GET_IP(regs) ((regs)->ip)
179175
#define GET_FP(regs) ((regs)->bp)
@@ -201,14 +197,6 @@ static inline unsigned long regs_get_register(struct pt_regs *regs,
201197
if (unlikely(offset > MAX_REG_OFFSET))
202198
return 0;
203199
#ifdef CONFIG_X86_32
204-
/*
205-
* Traps from the kernel do not save sp and ss.
206-
* Use the helper function to retrieve sp.
207-
*/
208-
if (offset == offsetof(struct pt_regs, sp) &&
209-
regs->cs == __KERNEL_CS)
210-
return kernel_stack_pointer(regs);
211-
212200
/* The selector fields are 16-bit. */
213201
if (offset == offsetof(struct pt_regs, cs) ||
214202
offset == offsetof(struct pt_regs, ss) ||
@@ -234,8 +222,7 @@ static inline unsigned long regs_get_register(struct pt_regs *regs,
234222
static inline int regs_within_kernel_stack(struct pt_regs *regs,
235223
unsigned long addr)
236224
{
237-
return ((addr & ~(THREAD_SIZE - 1)) ==
238-
(kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1)));
225+
return ((addr & ~(THREAD_SIZE - 1)) == (regs->sp & ~(THREAD_SIZE - 1)));
239226
}
240227

241228
/**
@@ -249,7 +236,7 @@ static inline int regs_within_kernel_stack(struct pt_regs *regs,
249236
*/
250237
static inline unsigned long *regs_get_kernel_stack_nth_addr(struct pt_regs *regs, unsigned int n)
251238
{
252-
unsigned long *addr = (unsigned long *)kernel_stack_pointer(regs);
239+
unsigned long *addr = (unsigned long *)regs->sp;
253240

254241
addr += n;
255242
if (regs_within_kernel_stack(regs, (unsigned long)addr))

0 commit comments

Comments
 (0)