MdeModulePkg/DxeCore: Implement heap guard feature for UEFI

This feature makes use of paging mechanism to add a hidden (not present)
page just before and after the allocated memory block. If the code tries
to access memory outside of the allocated part, page fault exception will
be triggered.

This feature is controlled by three PCDs:

    gEfiMdeModulePkgTokenSpaceGuid.PcdHeapGuardPropertyMask
    gEfiMdeModulePkgTokenSpaceGuid.PcdHeapGuardPoolType
    gEfiMdeModulePkgTokenSpaceGuid.PcdHeapGuardPageType

BIT0 and BIT1 of PcdHeapGuardPropertyMask can be used to enable or disable
memory guard for page and pool respectively. PcdHeapGuardPoolType and/or
PcdHeapGuardPageType are used to enable or disable guard for specific type
of memory. For example, we can turn on guard only for EfiBootServicesData
and EfiRuntimeServicesData by setting the PCD with value 0x50.

Pool memory is not ususally integer multiple of one page, and is more likely
less than a page. There's no way to monitor the overflow at both top and
bottom of pool memory. BIT7 of PcdHeapGuardPropertyMask is used to control
how to position the head of pool memory so that it's easier to catch memory
overflow in memory growing direction or in decreasing direction.

Note1: Turning on heap guard, especially pool guard, will introduce too many
memory fragments. Windows 10 has a limitation in its boot loader, which
accepts at most 512 memory descriptors passed from BIOS. This will prevent
Windows 10 from booting if heap guard is enabled. The latest Linux
distribution with grub boot loader has no such issue. Normally it's not
recommended to enable this feature in production build of BIOS.

Note2: Don't enable this feature for NT32 emulation platform which doesn't
support paging.

Cc: Star Zeng <star.zeng@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Michael Kinney <michael.d.kinney@intel.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Suggested-by: Ayellet Wolman <ayellet.wolman@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Jian J Wang <jian.j.wang@intel.com>
Reviewed-by: Jiewen Yao <jiewen.yao@intel.com>
Regression-tested-by: Laszlo Ersek <lersek@redhat.com>
This commit is contained in:
Jian J Wang
2017-11-14 10:55:26 +08:00
committed by Star Zeng
parent 99cc7b9507
commit 235a4490c8
7 changed files with 1852 additions and 65 deletions

View File

@@ -14,6 +14,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#include "DxeMain.h"
#include "Imem.h"
#include "HeapGuard.h"
STATIC EFI_LOCK mPoolMemoryLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY);
@@ -169,7 +170,7 @@ LookupPoolHead (
}
}
Pool = CoreAllocatePoolI (EfiBootServicesData, sizeof (POOL));
Pool = CoreAllocatePoolI (EfiBootServicesData, sizeof (POOL), FALSE);
if (Pool == NULL) {
return NULL;
}
@@ -214,7 +215,8 @@ CoreInternalAllocatePool (
OUT VOID **Buffer
)
{
EFI_STATUS Status;
EFI_STATUS Status;
BOOLEAN NeedGuard;
//
// If it's not a valid type, fail it
@@ -238,6 +240,8 @@ CoreInternalAllocatePool (
return EFI_OUT_OF_RESOURCES;
}
NeedGuard = IsPoolTypeToGuard (PoolType) && !mOnGuarding;
//
// Acquire the memory lock and make the allocation
//
@@ -246,7 +250,7 @@ CoreInternalAllocatePool (
return EFI_OUT_OF_RESOURCES;
}
*Buffer = CoreAllocatePoolI (PoolType, Size);
*Buffer = CoreAllocatePoolI (PoolType, Size, NeedGuard);
CoreReleaseLock (&mPoolMemoryLock);
return (*Buffer != NULL) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES;
}
@@ -298,6 +302,7 @@ CoreAllocatePool (
@param PoolType The type of memory for the new pool pages
@param NoPages No of pages to allocate
@param Granularity Bits to align.
@param NeedGuard Flag to indicate Guard page is needed or not
@return The allocated memory, or NULL
@@ -307,7 +312,8 @@ VOID *
CoreAllocatePoolPagesI (
IN EFI_MEMORY_TYPE PoolType,
IN UINTN NoPages,
IN UINTN Granularity
IN UINTN Granularity,
IN BOOLEAN NeedGuard
)
{
VOID *Buffer;
@@ -318,11 +324,14 @@ CoreAllocatePoolPagesI (
return NULL;
}
Buffer = CoreAllocatePoolPages (PoolType, NoPages, Granularity);
Buffer = CoreAllocatePoolPages (PoolType, NoPages, Granularity, NeedGuard);
CoreReleaseMemoryLock ();
if (Buffer != NULL) {
ApplyMemoryProtectionPolicy (EfiConventionalMemory, PoolType,
if (NeedGuard) {
SetGuardForMemory ((EFI_PHYSICAL_ADDRESS)(UINTN)Buffer, NoPages);
}
ApplyMemoryProtectionPolicy(EfiConventionalMemory, PoolType,
(EFI_PHYSICAL_ADDRESS)(UINTN)Buffer, EFI_PAGES_TO_SIZE (NoPages));
}
return Buffer;
@@ -334,6 +343,7 @@ CoreAllocatePoolPagesI (
@param PoolType Type of pool to allocate
@param Size The amount of pool to allocate
@param NeedGuard Flag to indicate Guard page is needed or not
@return The allocate pool, or NULL
@@ -341,7 +351,8 @@ CoreAllocatePoolPagesI (
VOID *
CoreAllocatePoolI (
IN EFI_MEMORY_TYPE PoolType,
IN UINTN Size
IN UINTN Size,
IN BOOLEAN NeedGuard
)
{
POOL *Pool;
@@ -355,6 +366,7 @@ CoreAllocatePoolI (
UINTN Offset, MaxOffset;
UINTN NoPages;
UINTN Granularity;
BOOLEAN HasPoolTail;
ASSERT_LOCKED (&mPoolMemoryLock);
@@ -372,6 +384,9 @@ CoreAllocatePoolI (
// Adjust the size by the pool header & tail overhead
//
HasPoolTail = !(NeedGuard &&
((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) == 0));
//
// Adjusting the Size to be of proper alignment so that
// we don't get an unaligned access fault later when
@@ -391,10 +406,16 @@ CoreAllocatePoolI (
// If allocation is over max size, just allocate pages for the request
// (slow)
//
if (Index >= SIZE_TO_LIST (Granularity)) {
NoPages = EFI_SIZE_TO_PAGES(Size) + EFI_SIZE_TO_PAGES (Granularity) - 1;
if (Index >= SIZE_TO_LIST (Granularity) || NeedGuard) {
if (!HasPoolTail) {
Size -= sizeof (POOL_TAIL);
}
NoPages = EFI_SIZE_TO_PAGES (Size) + EFI_SIZE_TO_PAGES (Granularity) - 1;
NoPages &= ~(UINTN)(EFI_SIZE_TO_PAGES (Granularity) - 1);
Head = CoreAllocatePoolPagesI (PoolType, NoPages, Granularity);
Head = CoreAllocatePoolPagesI (PoolType, NoPages, Granularity, NeedGuard);
if (NeedGuard) {
Head = AdjustPoolHeadA ((EFI_PHYSICAL_ADDRESS)(UINTN)Head, NoPages, Size);
}
goto Done;
}
@@ -422,7 +443,8 @@ CoreAllocatePoolI (
//
// Get another page
//
NewPage = CoreAllocatePoolPagesI (PoolType, EFI_SIZE_TO_PAGES (Granularity), Granularity);
NewPage = CoreAllocatePoolPagesI (PoolType, EFI_SIZE_TO_PAGES (Granularity),
Granularity, NeedGuard);
if (NewPage == NULL) {
goto Done;
}
@@ -468,30 +490,39 @@ Done:
if (Head != NULL) {
//
// Account the allocation
//
Pool->Used += Size;
//
// If we have a pool buffer, fill in the header & tail info
//
Head->Signature = POOL_HEAD_SIGNATURE;
Head->Size = Size;
Head->Type = (EFI_MEMORY_TYPE) PoolType;
Tail = HEAD_TO_TAIL (Head);
Tail->Signature = POOL_TAIL_SIGNATURE;
Tail->Size = Size;
Buffer = Head->Data;
DEBUG_CLEAR_MEMORY (Buffer, Size - POOL_OVERHEAD);
if (HasPoolTail) {
Tail = HEAD_TO_TAIL (Head);
Tail->Signature = POOL_TAIL_SIGNATURE;
Tail->Size = Size;
Size -= POOL_OVERHEAD;
} else {
Size -= SIZE_OF_POOL_HEAD;
}
DEBUG_CLEAR_MEMORY (Buffer, Size);
DEBUG ((
DEBUG_POOL,
"AllocatePoolI: Type %x, Addr %p (len %lx) %,ld\n", PoolType,
Buffer,
(UINT64)(Size - POOL_OVERHEAD),
(UINT64)Size,
(UINT64) Pool->Used
));
//
// Account the allocation
//
Pool->Used += Size;
} else {
DEBUG ((DEBUG_ERROR | DEBUG_POOL, "AllocatePool: failed to allocate %ld bytes\n", (UINT64) Size));
@@ -588,6 +619,34 @@ CoreFreePoolPagesI (
(EFI_PHYSICAL_ADDRESS)(UINTN)Memory, EFI_PAGES_TO_SIZE (NoPages));
}
/**
Internal function. Frees guarded pool pages.
@param PoolType The type of memory for the pool pages
@param Memory The base address to free
@param NoPages The number of pages to free
**/
STATIC
VOID
CoreFreePoolPagesWithGuard (
IN EFI_MEMORY_TYPE PoolType,
IN EFI_PHYSICAL_ADDRESS Memory,
IN UINTN NoPages
)
{
EFI_PHYSICAL_ADDRESS MemoryGuarded;
UINTN NoPagesGuarded;
MemoryGuarded = Memory;
NoPagesGuarded = NoPages;
AdjustMemoryF (&Memory, &NoPages);
CoreFreePoolPagesI (PoolType, Memory, NoPages);
UnsetGuardForMemory (MemoryGuarded, NoPagesGuarded);
}
/**
Internal function to free a pool entry.
Caller must have the memory lock held
@@ -616,6 +675,8 @@ CoreFreePoolI (
UINTN Offset;
BOOLEAN AllFree;
UINTN Granularity;
BOOLEAN IsGuarded;
BOOLEAN HasPoolTail;
ASSERT(Buffer != NULL);
//
@@ -628,24 +689,32 @@ CoreFreePoolI (
return EFI_INVALID_PARAMETER;
}
Tail = HEAD_TO_TAIL (Head);
ASSERT(Tail != NULL);
IsGuarded = IsPoolTypeToGuard (Head->Type) &&
IsMemoryGuarded ((EFI_PHYSICAL_ADDRESS)(UINTN)Head);
HasPoolTail = !(IsGuarded &&
((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) == 0));
//
// Debug
//
ASSERT (Tail->Signature == POOL_TAIL_SIGNATURE);
ASSERT (Head->Size == Tail->Size);
ASSERT_LOCKED (&mPoolMemoryLock);
if (HasPoolTail) {
Tail = HEAD_TO_TAIL (Head);
ASSERT (Tail != NULL);
if (Tail->Signature != POOL_TAIL_SIGNATURE) {
return EFI_INVALID_PARAMETER;
}
if (Head->Size != Tail->Size) {
return EFI_INVALID_PARAMETER;
//
// Debug
//
ASSERT (Tail->Signature == POOL_TAIL_SIGNATURE);
ASSERT (Head->Size == Tail->Size);
if (Tail->Signature != POOL_TAIL_SIGNATURE) {
return EFI_INVALID_PARAMETER;
}
if (Head->Size != Tail->Size) {
return EFI_INVALID_PARAMETER;
}
}
ASSERT_LOCKED (&mPoolMemoryLock);
//
// Determine the pool type and account for it
//
@@ -680,14 +749,27 @@ CoreFreePoolI (
//
// If it's not on the list, it must be pool pages
//
if (Index >= SIZE_TO_LIST (Granularity)) {
if (Index >= SIZE_TO_LIST (Granularity) || IsGuarded) {
//
// Return the memory pages back to free memory
//
NoPages = EFI_SIZE_TO_PAGES(Size) + EFI_SIZE_TO_PAGES (Granularity) - 1;
NoPages = EFI_SIZE_TO_PAGES (Size) + EFI_SIZE_TO_PAGES (Granularity) - 1;
NoPages &= ~(UINTN)(EFI_SIZE_TO_PAGES (Granularity) - 1);
CoreFreePoolPagesI (Pool->MemoryType, (EFI_PHYSICAL_ADDRESS) (UINTN) Head, NoPages);
if (IsGuarded) {
Head = AdjustPoolHeadF ((EFI_PHYSICAL_ADDRESS)(UINTN)Head);
CoreFreePoolPagesWithGuard (
Pool->MemoryType,
(EFI_PHYSICAL_ADDRESS)(UINTN)Head,
NoPages
);
} else {
CoreFreePoolPagesI (
Pool->MemoryType,
(EFI_PHYSICAL_ADDRESS)(UINTN)Head,
NoPages
);
}
} else {