Skip to content

Commit 8954290

Browse files
author
Peter Zijlstra
committed
x86/entry/32: Fix NMI vs ESPFIX
When the NMI lands on an ESPFIX_SS, we are on the entry stack and must swizzle, otherwise we'll run do_nmi() on the entry stack, which is BAD. Also, similar to the normal exception path, we need to correct the ESPFIX magic before leaving the entry stack, otherwise pt_regs will present a non-flat stack pointer. Tested by running sigreturn_32 concurrent with perf-record. Fixes: e5862d0 ("x86/entry/32: Leave the kernel via trampoline stack") Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Acked-by: Andy Lutomirski <[email protected]> Cc: [email protected]
1 parent a1a338e commit 8954290

File tree

1 file changed

+41
-12
lines changed

1 file changed

+41
-12
lines changed

arch/x86/entry/entry_32.S

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@
205205
#define CS_FROM_ENTRY_STACK (1 << 31)
206206
#define CS_FROM_USER_CR3 (1 << 30)
207207
#define CS_FROM_KERNEL (1 << 29)
208+
#define CS_FROM_ESPFIX (1 << 28)
208209

209210
.macro FIXUP_FRAME
210211
/*
@@ -342,8 +343,8 @@
342343
.endif
343344
.endm
344345

345-
.macro SAVE_ALL_NMI cr3_reg:req
346-
SAVE_ALL
346+
.macro SAVE_ALL_NMI cr3_reg:req unwind_espfix=0
347+
SAVE_ALL unwind_espfix=\unwind_espfix
347348

348349
BUG_IF_WRONG_CR3
349350

@@ -1526,6 +1527,10 @@ ENTRY(nmi)
15261527
ASM_CLAC
15271528

15281529
#ifdef CONFIG_X86_ESPFIX32
1530+
/*
1531+
* ESPFIX_SS is only ever set on the return to user path
1532+
* after we've switched to the entry stack.
1533+
*/
15291534
pushl %eax
15301535
movl %ss, %eax
15311536
cmpw $__ESPFIX_SS, %ax
@@ -1561,30 +1566,54 @@ ENTRY(nmi)
15611566
movl %ebx, %esp
15621567

15631568
.Lnmi_return:
1569+
#ifdef CONFIG_X86_ESPFIX32
1570+
testl $CS_FROM_ESPFIX, PT_CS(%esp)
1571+
jnz .Lnmi_from_espfix
1572+
#endif
1573+
15641574
CHECK_AND_APPLY_ESPFIX
15651575
RESTORE_ALL_NMI cr3_reg=%edi pop=4
15661576
jmp .Lirq_return
15671577

15681578
#ifdef CONFIG_X86_ESPFIX32
15691579
.Lnmi_espfix_stack:
15701580
/*
1571-
* create the pointer to lss back
1581+
* Create the pointer to LSS back
15721582
*/
15731583
pushl %ss
15741584
pushl %esp
15751585
addl $4, (%esp)
1576-
/* copy the iret frame of 12 bytes */
1577-
.rept 3
1578-
pushl 16(%esp)
1579-
.endr
1580-
pushl %eax
1581-
SAVE_ALL_NMI cr3_reg=%edi
1586+
1587+
/* Copy the (short) IRET frame */
1588+
pushl 4*4(%esp) # flags
1589+
pushl 4*4(%esp) # cs
1590+
pushl 4*4(%esp) # ip
1591+
1592+
pushl %eax # orig_ax
1593+
1594+
SAVE_ALL_NMI cr3_reg=%edi unwind_espfix=1
15821595
ENCODE_FRAME_POINTER
1583-
FIXUP_ESPFIX_STACK # %eax == %esp
1596+
1597+
/* clear CS_FROM_KERNEL, set CS_FROM_ESPFIX */
1598+
xorl $(CS_FROM_ESPFIX | CS_FROM_KERNEL), PT_CS(%esp)
1599+
15841600
xorl %edx, %edx # zero error code
1585-
call do_nmi
1601+
movl %esp, %eax # pt_regs pointer
1602+
jmp .Lnmi_from_sysenter_stack
1603+
1604+
.Lnmi_from_espfix:
15861605
RESTORE_ALL_NMI cr3_reg=%edi
1587-
lss 12+4(%esp), %esp # back to espfix stack
1606+
/*
1607+
* Because we cleared CS_FROM_KERNEL, IRET_FRAME 'forgot' to
1608+
* fix up the gap and long frame:
1609+
*
1610+
* 3 - original frame (exception)
1611+
* 2 - ESPFIX block (above)
1612+
* 6 - gap (FIXUP_FRAME)
1613+
* 5 - long frame (FIXUP_FRAME)
1614+
* 1 - orig_ax
1615+
*/
1616+
lss (1+5+6)*4(%esp), %esp # back to espfix stack
15881617
jmp .Lirq_return
15891618
#endif
15901619
END(nmi)

0 commit comments

Comments
 (0)