UefiCpuPkg/PiSmmCpuDxeSmm: Use SMM Interrupt Shadow Stack

When CET shadow stack feature is enabled, it needs to use IST for the
 exceptions, and uses interrupt shadow stack for the stack switch.
Shadow stack should be 32 bytes aligned.
Check IST field, when clear shadow stack token busy bit when using retf.

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3728

Signed-off-by: Sheng Wei <w.sheng@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Rahul Kumar <rahul1.kumar@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
This commit is contained in:
Sheng, W 2021-11-12 09:40:28 +08:00 committed by mergify[bot]
parent 466ebdd2e0
commit 455b0347a7
5 changed files with 157 additions and 77 deletions

View File

@ -15,17 +15,36 @@
;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------
%include "Nasm.inc" %include "Nasm.inc"
;
; Equivalent NASM structure of IA32_DESCRIPTOR
;
struc IA32_DESCRIPTOR
.Limit CTYPE_UINT16 1
.Base CTYPE_UINTN 1
endstruc
;
; Equivalent NASM structure of IA32_IDT_GATE_DESCRIPTOR
;
struc IA32_IDT_GATE_DESCRIPTOR
.OffsetLow CTYPE_UINT16 1
.Selector CTYPE_UINT16 1
.Reserved_0 CTYPE_UINT8 1
.GateType CTYPE_UINT8 1
.OffsetHigh CTYPE_UINT16 1
.OffsetUpper CTYPE_UINT32 1
.Reserved_1 CTYPE_UINT32 1
endstruc
; ;
; CommonExceptionHandler() ; CommonExceptionHandler()
; ;
%define VC_EXCEPTION 29 %define VC_EXCEPTION 29
%define PF_EXCEPTION 14
extern ASM_PFX(mErrorCodeFlag) ; Error code flags for exceptions extern ASM_PFX(mErrorCodeFlag) ; Error code flags for exceptions
extern ASM_PFX(mDoFarReturnFlag) ; Do far return flag extern ASM_PFX(mDoFarReturnFlag) ; Do far return flag
extern ASM_PFX(CommonExceptionHandler) extern ASM_PFX(CommonExceptionHandler)
extern ASM_PFX(FeaturePcdGet (PcdCpuSmmStackGuard))
SECTION .data SECTION .data
@ -282,42 +301,49 @@ DrFinish:
; The follow algorithm is used for clear shadow stack token busy bit. ; The follow algorithm is used for clear shadow stack token busy bit.
; The comment is based on the sample shadow stack. ; The comment is based on the sample shadow stack.
; Shadow stack is 32 bytes aligned.
; The sample shadow stack layout : ; The sample shadow stack layout :
; Address | Context ; Address | Context
; +-------------------------+ ; +-------------------------+
; 0xFD0 | FREE | it is 0xFD8|0x02|(LMA & CS.L), after SAVEPREVSSP. ; 0xFB8 | FREE | It is 0xFC0|0x02|(LMA & CS.L), after SAVEPREVSSP.
; +-------------------------+ ; +-------------------------+
; 0xFD8 | Prev SSP | ; 0xFC0 | Prev SSP |
; +-------------------------+ ; +-------------------------+
; 0xFE0 | RIP | ; 0xFC8 | RIP |
; +-------------------------+ ; +-------------------------+
; 0xFE8 | CS | ; 0xFD0 | CS |
; +-------------------------+ ; +-------------------------+
; 0xFF0 | 0xFF0 | BUSY | BUSY flag cleared after CLRSSBSY ; 0xFD8 | 0xFD8 | BUSY | BUSY flag cleared after CLRSSBSY
; +-------------------------+ ; +-------------------------+
; 0xFF8 | 0xFD8|0x02|(LMA & CS.L) | ; 0xFE0 | 0xFC0|0x02|(LMA & CS.L) |
; +-------------------------+ ; +-------------------------+
; Instructions for Intel Control Flow Enforcement Technology (CET) are supported since NASM version 2.15.01. ; Instructions for Intel Control Flow Enforcement Technology (CET) are supported since NASM version 2.15.01.
cmp qword [ASM_PFX(mDoFarReturnFlag)], 0 cmp qword [ASM_PFX(mDoFarReturnFlag)], 0
jz CetDone jz CetDone
cmp qword [rbp + 8], PF_EXCEPTION ; check if it is a Page Fault
jnz CetDone
cmp byte [dword ASM_PFX(FeaturePcdGet (PcdCpuSmmStackGuard))], 0
jz CetDone
mov rax, cr4 mov rax, cr4
and rax, 0x800000 ; check if CET is enabled and rax, 0x800000 ; Check if CET is enabled
jz CetDone jz CetDone
; SSP should be 0xFD8 at this point sub rsp, 0x10
sidt [rsp]
mov rcx, qword [rsp + IA32_DESCRIPTOR.Base]; Get IDT base address
add rsp, 0x10
mov rax, qword [rbp + 8]; Get exception number
sal rax, 0x04 ; Get IDT offset
add rax, rcx ; Get IDT gate descriptor address
mov al, byte [rax + IA32_IDT_GATE_DESCRIPTOR.Reserved_0]
and rax, 0x01 ; Check IST field
jz CetDone
; SSP should be 0xFC0 at this point
mov rax, 0x04 ; advance past cs:lip:prevssp;supervisor shadow stack token mov rax, 0x04 ; advance past cs:lip:prevssp;supervisor shadow stack token
INCSSP_RAX ; After this SSP should be 0xFF8 INCSSP_RAX ; After this SSP should be 0xFE0
SAVEPREVSSP ; now the shadow stack restore token will be created at 0xFD0 SAVEPREVSSP ; now the shadow stack restore token will be created at 0xFB8
READSSP_RAX ; Read new SSP, SSP should be 0x1000 READSSP_RAX ; Read new SSP, SSP should be 0xFE8
sub rax, 0x10 sub rax, 0x10
CLRSSBSY_RAX ; Clear token at 0xFF0, SSP should be 0 after this CLRSSBSY_RAX ; Clear token at 0xFD8, SSP should be 0 after this
sub rax, 0x20 sub rax, 0x20
RSTORSSP_RAX ; Restore to token at 0xFD0, new SSP will be 0xFD0 RSTORSSP_RAX ; Restore to token at 0xFB8, new SSP will be 0xFB8
mov rax, 0x01 ; Pop off the new save token created mov rax, 0x01 ; Pop off the new save token created
INCSSP_RAX ; SSP should be 0xFD8 now INCSSP_RAX ; SSP should be 0xFC0 now
CetDone: CetDone:
cli cli

View File

@ -861,35 +861,58 @@ PiCpuSmmEntry (
mSmmStackSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmStackSize))); mSmmStackSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmStackSize)));
if (FeaturePcdGet (PcdCpuSmmStackGuard)) { if (FeaturePcdGet (PcdCpuSmmStackGuard)) {
// //
// 2 more pages is allocated for each processor. // SMM Stack Guard Enabled
// one is guard page and the other is known good stack. // 2 more pages is allocated for each processor, one is guard page and the other is known good stack.
// //
// +-------------------------------------------+-----+-------------------------------------------+ // +--------------------------------------------------+-----+--------------------------------------------------+
// | Known Good Stack | Guard Page | SMM Stack | ... | Known Good Stack | Guard Page | SMM Stack | // | Known Good Stack | Guard Page | SMM Stack | ... | Known Good Stack | Guard Page | SMM Stack |
// +-------------------------------------------+-----+-------------------------------------------+ // +--------------------------------------------------+-----+--------------------------------------------------+
// | 4K | 4K PcdCpuSmmStackSize| | 4K | 4K PcdCpuSmmStackSize|
// |<---------------- mSmmStackSize ----------------->| |<---------------- mSmmStackSize ----------------->|
// | | | | // | | | |
// |<-------------- Processor 0 -------------->| |<-------------- Processor n -------------->| // |<------------------ Processor 0 ----------------->| |<------------------ Processor n ----------------->|
// //
mSmmStackSize += EFI_PAGES_TO_SIZE (2); mSmmStackSize += EFI_PAGES_TO_SIZE (2);
} }
mSmmShadowStackSize = 0; mSmmShadowStackSize = 0;
if ((PcdGet32 (PcdControlFlowEnforcementPropertyMask) != 0) && mCetSupported) { if ((PcdGet32 (PcdControlFlowEnforcementPropertyMask) != 0) && mCetSupported) {
mSmmShadowStackSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmShadowStackSize)));
if (FeaturePcdGet (PcdCpuSmmStackGuard)) {
// //
// SMM Stack Guard Enabled
// Append Shadow Stack after normal stack // Append Shadow Stack after normal stack
// 2 more pages is allocated for each processor, one is guard page and the other is known good shadow stack.
// //
// |= Stacks // |= Stacks
// +--------------------------------------------------+---------------------------------------------------------------+ // +--------------------------------------------------+---------------------------------------------------------------+
// | Known Good Stack | Guard Page | SMM Stack | Known Good Shadow Stack | Guard Page | SMM Shadow Stack | // | Known Good Stack | Guard Page | SMM Stack | Known Good Shadow Stack | Guard Page | SMM Shadow Stack |
// +--------------------------------------------------+---------------------------------------------------------------+ // +--------------------------------------------------+---------------------------------------------------------------+
// | |PcdCpuSmmStackSize| |PcdCpuSmmShadowStackSize| // | 4K | 4K |PcdCpuSmmStackSize| 4K | 4K |PcdCpuSmmShadowStackSize|
// |<---------------- mSmmStackSize ----------------->|<--------------------- mSmmShadowStackSize ------------------->| // |<---------------- mSmmStackSize ----------------->|<--------------------- mSmmShadowStackSize ------------------->|
// | | // | |
// |<-------------------------------------------- Processor N ------------------------------------------------------->| // |<-------------------------------------------- Processor N ------------------------------------------------------->|
// //
mSmmShadowStackSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmShadowStackSize)));
if (FeaturePcdGet (PcdCpuSmmStackGuard)) {
mSmmShadowStackSize += EFI_PAGES_TO_SIZE (2); mSmmShadowStackSize += EFI_PAGES_TO_SIZE (2);
} else {
//
// SMM Stack Guard Disabled (Known Good Stack is still required for potential stack switch.)
// Append Shadow Stack after normal stack with 1 more page as known good shadow stack.
// 1 more pages is allocated for each processor, it is known good stack.
//
//
// |= Stacks
// +-------------------------------------+--------------------------------------------------+
// | Known Good Stack | SMM Stack | Known Good Shadow Stack | SMM Shadow Stack |
// +-------------------------------------+--------------------------------------------------+
// | 4K |PcdCpuSmmStackSize| 4K |PcdCpuSmmShadowStackSize|
// |<---------- mSmmStackSize ---------->|<--------------- mSmmShadowStackSize ------------>|
// | |
// |<-------------------------------- Processor N ----------------------------------------->|
//
mSmmShadowStackSize += EFI_PAGES_TO_SIZE (1);
mSmmStackSize += EFI_PAGES_TO_SIZE (1);
} }
} }

View File

@ -557,6 +557,20 @@ InitializeIDTSmmStackGuard (
VOID VOID
); );
/**
Initialize IDT IST Field.
@param[in] ExceptionType Exception type.
@param[in] Ist IST value.
**/
VOID
EFIAPI
InitializeIdtIst (
IN EFI_EXCEPTION_TYPE ExceptionType,
IN UINT8 Ist
);
/** /**
Initialize Gdt for all processors. Initialize Gdt for all processors.

View File

@ -481,7 +481,17 @@ SmmInitPageTable (
// Additional SMM IDT initialization for SMM stack guard // Additional SMM IDT initialization for SMM stack guard
// //
if (FeaturePcdGet (PcdCpuSmmStackGuard)) { if (FeaturePcdGet (PcdCpuSmmStackGuard)) {
InitializeIDTSmmStackGuard (); DEBUG ((DEBUG_INFO, "Initialize IDT IST field for SMM Stack Guard\n"));
InitializeIdtIst (EXCEPT_IA32_PAGE_FAULT, 1);
}
//
// Additional SMM IDT initialization for SMM CET shadow stack
//
if ((PcdGet32 (PcdControlFlowEnforcementPropertyMask) != 0) && mCetSupported) {
DEBUG ((DEBUG_INFO, "Initialize IDT IST field for SMM Shadow Stack\n"));
InitializeIdtIst (EXCEPT_IA32_PAGE_FAULT, 1);
InitializeIdtIst (EXCEPT_IA32_MACHINE_CHECK, 1);
} }
// //

View File

@ -24,24 +24,24 @@ UINT32 mCetInterruptSspTable;
UINTN mSmmInterruptSspTables; UINTN mSmmInterruptSspTables;
/** /**
Initialize IDT for SMM Stack Guard. Initialize IDT IST Field.
@param[in] ExceptionType Exception type.
@param[in] Ist IST value.
**/ **/
VOID VOID
EFIAPI EFIAPI
InitializeIDTSmmStackGuard ( InitializeIdtIst (
VOID IN EFI_EXCEPTION_TYPE ExceptionType,
IN UINT8 Ist
) )
{ {
IA32_IDT_GATE_DESCRIPTOR *IdtGate; IA32_IDT_GATE_DESCRIPTOR *IdtGate;
//
// If SMM Stack Guard feature is enabled, set the IST field of
// the interrupt gate for Page Fault Exception to be 1
//
IdtGate = (IA32_IDT_GATE_DESCRIPTOR *)gcSmiIdtr.Base; IdtGate = (IA32_IDT_GATE_DESCRIPTOR *)gcSmiIdtr.Base;
IdtGate += EXCEPT_IA32_PAGE_FAULT; IdtGate += ExceptionType;
IdtGate->Bits.Reserved_0 = 1; IdtGate->Bits.Reserved_0 = Ist;
} }
/** /**
@ -89,7 +89,7 @@ InitGdt (
GdtDescriptor->Bits.BaseMid = (UINT8)((UINTN)TssBase >> 16); GdtDescriptor->Bits.BaseMid = (UINT8)((UINTN)TssBase >> 16);
GdtDescriptor->Bits.BaseHigh = (UINT8)((UINTN)TssBase >> 24); GdtDescriptor->Bits.BaseHigh = (UINT8)((UINTN)TssBase >> 24);
if (FeaturePcdGet (PcdCpuSmmStackGuard)) { if ((FeaturePcdGet (PcdCpuSmmStackGuard)) || ((PcdGet32 (PcdControlFlowEnforcementPropertyMask) != 0) && mCetSupported)) {
// //
// Setup top of known good stack as IST1 for each processor. // Setup top of known good stack as IST1 for each processor.
// //
@ -177,8 +177,16 @@ InitShadowStack (
if ((PcdGet32 (PcdControlFlowEnforcementPropertyMask) != 0) && mCetSupported) { if ((PcdGet32 (PcdControlFlowEnforcementPropertyMask) != 0) && mCetSupported) {
SmmShadowStackSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmShadowStackSize))); SmmShadowStackSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmShadowStackSize)));
//
// Add 1 page as known good shadow stack
//
SmmShadowStackSize += EFI_PAGES_TO_SIZE (1);
if (FeaturePcdGet (PcdCpuSmmStackGuard)) { if (FeaturePcdGet (PcdCpuSmmStackGuard)) {
SmmShadowStackSize += EFI_PAGES_TO_SIZE (2); //
// Add one guard page between Known Good Shadow Stack and SMM Shadow Stack.
//
SmmShadowStackSize += EFI_PAGES_TO_SIZE (1);
} }
mCetPl0Ssp = (UINT32)((UINTN)ShadowStack + SmmShadowStackSize - sizeof(UINT64)); mCetPl0Ssp = (UINT32)((UINTN)ShadowStack + SmmShadowStackSize - sizeof(UINT64));
PatchInstructionX86 (mPatchCetPl0Ssp, mCetPl0Ssp, 4); PatchInstructionX86 (mPatchCetPl0Ssp, mCetPl0Ssp, 4);
@ -186,7 +194,6 @@ InitShadowStack (
DEBUG ((DEBUG_INFO, "ShadowStack - 0x%x\n", ShadowStack)); DEBUG ((DEBUG_INFO, "ShadowStack - 0x%x\n", ShadowStack));
DEBUG ((DEBUG_INFO, " SmmShadowStackSize - 0x%x\n", SmmShadowStackSize)); DEBUG ((DEBUG_INFO, " SmmShadowStackSize - 0x%x\n", SmmShadowStackSize));
if (FeaturePcdGet (PcdCpuSmmStackGuard)) {
if (mSmmInterruptSspTables == 0) { if (mSmmInterruptSspTables == 0) {
mSmmInterruptSspTables = (UINTN)AllocateZeroPool(sizeof(UINT64) * 8 * gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus); mSmmInterruptSspTables = (UINTN)AllocateZeroPool(sizeof(UINT64) * 8 * gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus);
ASSERT (mSmmInterruptSspTables != 0); ASSERT (mSmmInterruptSspTables != 0);
@ -194,14 +201,15 @@ InitShadowStack (
} }
// //
// The highest address on the stack (0xFF8) is a save-previous-ssp token pointing to a location that is 40 bytes away - 0xFD0. // The highest address on the stack (0xFE0) is a save-previous-ssp token pointing to a location that is 40 bytes away - 0xFB8.
// The supervisor shadow stack token is just above it at address 0xFF0. This is where the interrupt SSP table points. // The supervisor shadow stack token is just above it at address 0xFD8. This is where the interrupt SSP table points.
// So when an interrupt of exception occurs, we can use SAVESSP/RESTORESSP/CLEARSSBUSY for the supervisor shadow stack, // So when an interrupt of exception occurs, we can use SAVESSP/RESTORESSP/CLEARSSBUSY for the supervisor shadow stack,
// due to the reason the RETF in SMM exception handler cannot clear the BUSY flag with same CPL. // due to the reason the RETF in SMM exception handler cannot clear the BUSY flag with same CPL.
// (only IRET or RETF with different CPL can clear BUSY flag) // (only IRET or RETF with different CPL can clear BUSY flag)
// Please refer to UefiCpuPkg/Library/CpuExceptionHandlerLib/X64 for the full stack frame at runtime. // Please refer to UefiCpuPkg/Library/CpuExceptionHandlerLib/X64 for the full stack frame at runtime.
// According to SDM (ver. 075 June 2021), shadow stack should be 32 bytes aligned.
// //
InterruptSsp = (UINT32)((UINTN)ShadowStack + EFI_PAGES_TO_SIZE(1) - sizeof(UINT64)); InterruptSsp = (UINT32)(((UINTN)ShadowStack + EFI_PAGES_TO_SIZE(1) - (sizeof(UINT64) * 4)) & ~0x1f);
*(UINT64 *)(UINTN)InterruptSsp = (InterruptSsp - sizeof(UINT64) * 4) | 0x2; *(UINT64 *)(UINTN)InterruptSsp = (InterruptSsp - sizeof(UINT64) * 4) | 0x2;
mCetInterruptSsp = InterruptSsp - sizeof(UINT64); mCetInterruptSsp = InterruptSsp - sizeof(UINT64);
@ -213,6 +221,5 @@ InitShadowStack (
DEBUG ((DEBUG_INFO, "mCetInterruptSsp - 0x%x\n", mCetInterruptSsp)); DEBUG ((DEBUG_INFO, "mCetInterruptSsp - 0x%x\n", mCetInterruptSsp));
DEBUG ((DEBUG_INFO, "mCetInterruptSspTable - 0x%x\n", mCetInterruptSspTable)); DEBUG ((DEBUG_INFO, "mCetInterruptSspTable - 0x%x\n", mCetInterruptSspTable));
} }
}
} }