Skip to content

Commit

Permalink
UefiCpuPkg/CpuDxe: implement non-stop mode for uefi
Browse files Browse the repository at this point in the history
Same as SMM profile feature, a special #PF is used to set page attribute
to 'present' and a special #DB handler to reset it back to 'not-present',
right after the instruction causing #PF got executed.

Since the new #PF handler won't enter into dead-loop, the instruction
which caused the #PF will get chance to re-execute with accessible pages.

The exception message will still be printed out on debug console so that
the developer/QA can find that there's potential heap overflow or null
pointer access occurred.

Cc: Eric Dong <[email protected]>
Cc: Laszlo Ersek <[email protected]>
Cc: Ruiyu Ni <[email protected]>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Jian J Wang <[email protected]>
Reviewed-by: Eric Dong <[email protected]>
Acked-by: Laszlo Ersek <[email protected]>
  • Loading branch information
Jian J Wang committed Aug 29, 2018
1 parent 16b918b commit dcc0262
Show file tree
Hide file tree
Showing 4 changed files with 237 additions and 6 deletions.
39 changes: 39 additions & 0 deletions UefiCpuPkg/CpuDxe/CpuDxe.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@
EFI_MEMORY_RO \
)

#define HEAP_GUARD_NONSTOP_MODE \
((PcdGet8 (PcdHeapGuardPropertyMask) & (BIT6|BIT1|BIT0)) > BIT6)

#define NULL_DETECTION_NONSTOP_MODE \
((PcdGet8 (PcdNullPointerDetectionPropertyMask) & (BIT6|BIT0)) > BIT6)

/**
Flush CPU data cache. If the instruction cache is fully coherent
with all DMA operations then function can just return EFI_SUCCESS.
Expand Down Expand Up @@ -273,7 +279,40 @@ RefreshGcdMemoryAttributesFromPaging (
VOID
);

/**
Special handler for #DB exception, which will restore the page attributes
(not-present). It should work with #PF handler which will set pages to
'present'.
@param ExceptionType Exception type.
@param SystemContext Pointer to EFI_SYSTEM_CONTEXT.
**/
VOID
EFIAPI
DebugExceptionHandler (
IN EFI_EXCEPTION_TYPE InterruptType,
IN EFI_SYSTEM_CONTEXT SystemContext
);

/**
Special handler for #PF exception, which will set the pages which caused
#PF to be 'present'. The attribute of those pages should be restored in
the subsequent #DB handler.
@param ExceptionType Exception type.
@param SystemContext Pointer to EFI_SYSTEM_CONTEXT.
**/
VOID
EFIAPI
PageFaultExceptionHandler (
IN EFI_EXCEPTION_TYPE InterruptType,
IN EFI_SYSTEM_CONTEXT SystemContext
);

extern BOOLEAN mIsAllocatingPageTable;
extern UINTN mNumberOfProcessors;

#endif

3 changes: 3 additions & 0 deletions UefiCpuPkg/CpuDxe/CpuDxe.inf
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
ReportStatusCodeLib
MpInitLib
TimerLib
PeCoffGetEntryPointLib

[Sources]
CpuDxe.c
Expand Down Expand Up @@ -79,6 +80,8 @@
[Pcd]
gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask ## CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard ## CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdHeapGuardPropertyMask ## CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask ## CONSUMES
gUefiCpuPkgTokenSpaceGuid.PcdCpuStackSwitchExceptionList ## CONSUMES
gUefiCpuPkgTokenSpaceGuid.PcdCpuKnownGoodStackSize ## CONSUMES

Expand Down
34 changes: 28 additions & 6 deletions UefiCpuPkg/CpuDxe/CpuMp.c
Original file line number Diff line number Diff line change
Expand Up @@ -673,10 +673,6 @@ InitializeMpExceptionStackSwitchHandlers (
UINT8 *GdtBuffer;
UINT8 *StackTop;

if (!PcdGetBool (PcdCpuStackGuard)) {
return;
}

ExceptionNumber = FixedPcdGetSize (PcdCpuStackSwitchExceptionList);
NewStackSize = FixedPcdGet32 (PcdCpuKnownGoodStackSize) * ExceptionNumber;

Expand Down Expand Up @@ -790,6 +786,32 @@ InitializeMpExceptionStackSwitchHandlers (
}
}

/**
Initializes MP exceptions handlers for special features, such as Heap Guard
and Stack Guard.
**/
VOID
InitializeMpExceptionHandlers (
VOID
)
{
//
// Enable non-stop mode for #PF triggered by Heap Guard or NULL Pointer
// Detection.
//
if (HEAP_GUARD_NONSTOP_MODE || NULL_DETECTION_NONSTOP_MODE) {
RegisterCpuInterruptHandler (EXCEPT_IA32_DEBUG, DebugExceptionHandler);
RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, PageFaultExceptionHandler);
}

//
// Setup stack switch for Stack Guard feature.
//
if (PcdGetBool (PcdCpuStackGuard)) {
InitializeMpExceptionStackSwitchHandlers ();
}
}

/**
Initialize Multi-processor support.
Expand All @@ -814,9 +836,9 @@ InitializeMpSupport (
DEBUG ((DEBUG_INFO, "Detect CPU count: %d\n", mNumberOfProcessors));

//
// Initialize exception stack switch handlers for each logic processor.
// Initialize special exception handlers for each logic processor.
//
InitializeMpExceptionStackSwitchHandlers ();
InitializeMpExceptionHandlers ();

//
// Update CPU healthy information from Guided HOB
Expand Down
167 changes: 167 additions & 0 deletions UefiCpuPkg/CpuDxe/CpuPageTable.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
#include <Library/MemoryAllocationLib.h>
#include <Library/DebugLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/PeCoffGetEntryPointLib.h>
#include <Library/SerialPortLib.h>
#include <Library/SynchronizationLib.h>
#include <Library/PrintLib.h>
#include <Protocol/MpService.h>
#include <Protocol/SmmBase2.h>
#include <Register/Cpuid.h>
Expand Down Expand Up @@ -73,6 +77,10 @@
#define PAGING_2M_ADDRESS_MASK_64 0x000FFFFFFFE00000ull
#define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull

#define MAX_PF_ENTRY_COUNT 10
#define MAX_DEBUG_MESSAGE_LENGTH 0x100
#define IA32_PF_EC_ID BIT4

typedef enum {
PageNone,
Page4K,
Expand Down Expand Up @@ -102,6 +110,12 @@ PAGE_TABLE_POOL *mPageTablePool = NULL;
PAGE_TABLE_LIB_PAGING_CONTEXT mPagingContext;
EFI_SMM_BASE2_PROTOCOL *mSmmBase2 = NULL;

//
// Record the page fault exception count for one instruction execution.
//
UINTN *mPFEntryCount;
UINT64 *(*mLastPFEntryPointer)[MAX_PF_ENTRY_COUNT];

/**
Check if current execution environment is in SMM mode or not, via
EFI_SMM_BASE2_PROTOCOL.
Expand Down Expand Up @@ -1135,6 +1149,150 @@ AllocatePageTableMemory (
return Buffer;
}

/**
Special handler for #DB exception, which will restore the page attributes
(not-present). It should work with #PF handler which will set pages to
'present'.
@param ExceptionType Exception type.
@param SystemContext Pointer to EFI_SYSTEM_CONTEXT.
**/
VOID
EFIAPI
DebugExceptionHandler (
IN EFI_EXCEPTION_TYPE ExceptionType,
IN EFI_SYSTEM_CONTEXT SystemContext
)
{
UINTN CpuIndex;
UINTN PFEntry;
BOOLEAN IsWpEnabled;

MpInitLibWhoAmI (&CpuIndex);

//
// Clear last PF entries
//
IsWpEnabled = IsReadOnlyPageWriteProtected ();
if (IsWpEnabled) {
DisableReadOnlyPageWriteProtect ();
}

for (PFEntry = 0; PFEntry < mPFEntryCount[CpuIndex]; PFEntry++) {
if (mLastPFEntryPointer[CpuIndex][PFEntry] != NULL) {
*mLastPFEntryPointer[CpuIndex][PFEntry] &= ~IA32_PG_P;
}
}

if (IsWpEnabled) {
EnableReadOnlyPageWriteProtect ();
}

//
// Reset page fault exception count for next page fault.
//
mPFEntryCount[CpuIndex] = 0;

//
// Flush TLB
//
CpuFlushTlb ();

//
// Clear TF in EFLAGS
//
if (mPagingContext.MachineType == IMAGE_FILE_MACHINE_I386) {
SystemContext.SystemContextIa32->Eflags &= (UINT32)~BIT8;
} else {
SystemContext.SystemContextX64->Rflags &= (UINT64)~BIT8;
}
}

/**
Special handler for #PF exception, which will set the pages which caused
#PF to be 'present'. The attribute of those pages should be restored in
the subsequent #DB handler.
@param ExceptionType Exception type.
@param SystemContext Pointer to EFI_SYSTEM_CONTEXT.
**/
VOID
EFIAPI
PageFaultExceptionHandler (
IN EFI_EXCEPTION_TYPE ExceptionType,
IN EFI_SYSTEM_CONTEXT SystemContext
)
{
EFI_STATUS Status;
UINT64 PFAddress;
PAGE_TABLE_LIB_PAGING_CONTEXT PagingContext;
PAGE_ATTRIBUTE PageAttribute;
UINT64 Attributes;
UINT64 *PageEntry;
UINTN Index;
UINTN CpuIndex;
UINTN PageNumber;
BOOLEAN NonStopMode;

PFAddress = AsmReadCr2 () & ~EFI_PAGE_MASK;
if (PFAddress < BASE_4KB) {
NonStopMode = NULL_DETECTION_NONSTOP_MODE ? TRUE : FALSE;
} else {
NonStopMode = HEAP_GUARD_NONSTOP_MODE ? TRUE : FALSE;
}

if (NonStopMode) {
MpInitLibWhoAmI (&CpuIndex);
GetCurrentPagingContext (&PagingContext);
//
// Memory operation cross page boundary, like "rep mov" instruction, will
// cause infinite loop between this and Debug Trap handler. We have to make
// sure that current page and the page followed are both in PRESENT state.
//
PageNumber = 2;
while (PageNumber > 0) {
PageEntry = GetPageTableEntry (&PagingContext, PFAddress, &PageAttribute);
ASSERT(PageEntry != NULL);

if (PageEntry != NULL) {
Attributes = GetAttributesFromPageEntry (PageEntry);
if ((Attributes & EFI_MEMORY_RP) != 0) {
Attributes &= ~EFI_MEMORY_RP;
Status = AssignMemoryPageAttributes (&PagingContext, PFAddress,
EFI_PAGE_SIZE, Attributes, NULL);
if (!EFI_ERROR(Status)) {
Index = mPFEntryCount[CpuIndex];
//
// Re-retrieve page entry because above calling might update page
// table due to table split.
//
PageEntry = GetPageTableEntry (&PagingContext, PFAddress, &PageAttribute);
mLastPFEntryPointer[CpuIndex][Index++] = PageEntry;
mPFEntryCount[CpuIndex] = Index;
}
}
}

PFAddress += EFI_PAGE_SIZE;
--PageNumber;
}
}

//
// Initialize the serial port before dumping.
//
SerialPortInitialize ();
//
// Display ExceptionType, CPU information and Image information
//
DumpCpuContext (ExceptionType, SystemContext);
if (!NonStopMode) {
CpuDeadLoop ();
}
}

/**
Initialize the Page Table lib.
**/
Expand All @@ -1158,6 +1316,15 @@ InitializePageTableLib (
EnableReadOnlyPageWriteProtect ();
}

if (HEAP_GUARD_NONSTOP_MODE || NULL_DETECTION_NONSTOP_MODE) {
mPFEntryCount = (UINTN *)AllocateZeroPool (sizeof (UINTN) * mNumberOfProcessors);
ASSERT (mPFEntryCount != NULL);

mLastPFEntryPointer = (UINT64 *(*)[MAX_PF_ENTRY_COUNT])
AllocateZeroPool (sizeof (mLastPFEntryPointer[0]) * mNumberOfProcessors);
ASSERT (mLastPFEntryPointer != NULL);
}

DEBUG ((DEBUG_INFO, "CurrentPagingContext:\n", CurrentPagingContext.MachineType));
DEBUG ((DEBUG_INFO, " MachineType - 0x%x\n", CurrentPagingContext.MachineType));
DEBUG ((DEBUG_INFO, " PageTableBase - 0x%x\n", CurrentPagingContext.ContextData.X64.PageTableBase));
Expand Down

0 comments on commit dcc0262

Please sign in to comment.